diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..e4ce2ac0 --- /dev/null +++ b/.clang-format @@ -0,0 +1,308 @@ +# Configuration file for clang-format 23 +--- +AccessModifierOffset: -4 +AlignAfterOpenBracket: Align +AlignArrayOfStructures: None +AlignConsecutiveAssignments: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionDeclarations: false + AlignFunctionPointers: false + EnumAssignments: false + PadOperators: true +AlignConsecutiveBitFields: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionDeclarations: false + AlignFunctionPointers: false + EnumAssignments: false + PadOperators: false +AlignConsecutiveDeclarations: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionDeclarations: true + AlignFunctionPointers: false + EnumAssignments: false + PadOperators: false +AlignConsecutiveMacros: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionDeclarations: false + AlignFunctionPointers: false + EnumAssignments: false + PadOperators: false +AlignConsecutiveShortCaseStatements: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCaseArrows: false + AlignCaseColons: false +AlignConsecutiveTableGenBreakingDAGArgColons: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionDeclarations: false + AlignFunctionPointers: false + EnumAssignments: false + PadOperators: false +AlignConsecutiveTableGenCondOperatorColons: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionDeclarations: false + AlignFunctionPointers: false + EnumAssignments: false + PadOperators: false +AlignConsecutiveTableGenDefinitionColons: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionDeclarations: false + AlignFunctionPointers: false + EnumAssignments: false + PadOperators: false +AlignEscapedNewlines: Right +AlignOperands: Align +AlignTrailingComments: + AlignPPAndNotPP: false + Kind: Never + OverEmptyLines: 0 +AllowAllArgumentsOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowBreakBeforeNoexceptSpecifier: Never +AllowBreakBeforeQtProperty: false +AllowShortBlocksOnASingleLine: Empty +AllowShortCaseExpressionOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortCompoundRequirementOnASingleLine: false +AllowShortEnumsOnASingleLine: false +AllowShortFunctionsOnASingleLine: + Empty: true + Inline: false + Other: false +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: false +AllowShortNamespacesOnASingleLine: false +AllowShortRecordOnASingleLine: EmptyAndAttached +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakBeforeMultilineStrings: false +BinPackLongBracedList: true +BitFieldColonSpacing: Both +BracedInitializerIndentWidth: -1 +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterExternBlock: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakAdjacentStringLiterals: true +BreakAfterAttributes: Never +BreakAfterJavaFieldAnnotations: false +BreakAfterOpenBracketBracedList: true +BreakAfterOpenBracketFunction: false +BreakAfterOpenBracketIf: false +BreakAfterOpenBracketLoop: false +BreakAfterOpenBracketSwitch: false +BreakAfterReturnType: None +BreakArrays: true +BreakBeforeBinaryOperators: None +BreakBeforeCloseBracketBracedList: true +BreakBeforeCloseBracketFunction: false +BreakBeforeCloseBracketIf: false +BreakBeforeCloseBracketLoop: false +BreakBeforeCloseBracketSwitch: false +BreakBeforeConceptDeclarations: Always +BreakBeforeBraces: Attach +BreakBeforeInlineASMColon: OnlyMultiline +BreakBeforeReturnType: None +BreakBeforeTemplateCloser: false +BreakBeforeTernaryOperators: true +BreakBinaryOperations: + Default: Never +BreakConstructorInitializers: BeforeColon +BreakFunctionDeclarationParameters: false +BreakFunctionDefinitionParameters: false +BreakInheritanceList: BeforeColon +BreakStringLiterals: true +BreakTemplateDeclarations: MultiLine +ColumnLimit: 100 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: AlignFirstComment +DerivePointerAlignment: false +DisableFormat: false +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: LogicalBlock +EnumTrailingComma: Remove +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +IncludeBlocks: Preserve +IncludeIsMainRegex: '(Test)?$' +IncludeIsMainSourceRegex: '' +IndentAccessModifiers: false +IndentCaseBlocks: false +IndentCaseLabels: true +IndentExportBlock: true +IndentExternBlock: AfterExternBlock +IndentGotoLabels: OuterIndent +IndentPPDirectives: None +IndentRequiresClause: true +IndentWidth: 4 +IndentWrappedFunctionNames: false +InsertBraces: true +InsertNewlineAtEOF: true +InsertTrailingCommas: None +IntegerLiteralSeparator: + Binary: 0 + BinaryMinDigitsInsert: 0 + BinaryMaxDigitsRemove: 0 + Decimal: 0 + DecimalMinDigitsInsert: 0 + DecimalMaxDigitsRemove: 0 + Hex: 0 + HexMinDigitsInsert: 0 + HexMaxDigitsRemove: 0 + BinaryMinDigits: 0 + DecimalMinDigits: 0 + HexMinDigits: 0 +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLines: + AtEndOfFile: true + AtStartOfBlock: false + AtStartOfFile: true +KeepFormFeed: false +LambdaBodyIndentation: Signature +LineEnding: DeriveLF +MacroBlockBegin: '' +MacroBlockEnd: '' +MainIncludeChar: Quote +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: All +NumericLiteralCase: + ExponentLetter: Lower + HexDigit: Lower + Prefix: Lower + Suffix: Upper +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 2 +ObjCBreakBeforeNestedBlockParam: true +ObjCSpaceAfterMethodDeclarationPrefix: true +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +OneLineFormatOffRegex: '' +PackArguments: + BinPack: BinPack + BreakAfter: 0 +PackConstructorInitializers: BinPack +PackParameters: + BinPack: BinPack + BreakAfter: 0 +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakBeforeMemberAccess: 150 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakOpenParenthesis: 0 +PenaltyBreakScopeResolution: 500 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyIndentedWhitespace: 0 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Right +PPIndentWidth: -1 +QualifierAlignment: Leave +ReferenceAlignment: Pointer +ReflowComments: Always +RemoveBracesLLVM: false +RemoveEmptyLinesInUnwrappedLines: true +RemoveParentheses: Leave +RemoveSemicolon: false +RequiresClausePosition: OwnLine +RequiresExpressionIndentation: OuterScope +SeparateDefinitionBlocks: Always +ShortNamespaceLines: 1 +SkipMacroDefinitionBody: false +SortIncludes: + Enabled: true + IgnoreCase: false + IgnoreExtension: false +SortJavaStaticImport: Before +SortUsingDeclarations: LexicographicNumeric +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterOperatorKeyword: false +SpaceAfterTemplateKeyword: true +SpaceAroundPointerQualifiers: Default +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeEnumUnderlyingTypeColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeJsonColon: false +SpaceBeforeParens: ControlStatements +SpaceBeforeParensOptions: + AfterControlStatements: true + AfterForeachMacros: true + AfterFunctionDefinitionName: false + AfterFunctionDeclarationName: false + AfterIfMacros: true + AfterNot: false + AfterOverloadedOperator: false + AfterPlacementOperator: true + AfterRequiresInClause: false + AfterRequiresInExpression: false + BeforeNonEmptyParentheses: false +SpaceBeforeRangeBasedForLoopColon: true +SpaceBeforeSquareBrackets: false +SpaceInEmptyBraces: Never +SpacesBeforeTrailingComments: 1 +SpacesInAngles: Never +SpacesInContainerLiterals: true +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: -1 +SpacesInParens: Never +SpacesInParensOptions: + ExceptDoubleParentheses: false + InCStyleCasts: false + InConditionalStatements: false + InEmptyParentheses: false + Other: false +SpacesInSquareBrackets: false +Standard: Latest +TableGenBreakInsideDAGArg: DontBreak +TabWidth: 4 +UseTab: AlignWithSpaces +VerilogBreakBetweenInstancePorts: true +WrapNamespaceBodyWithEmptyLines: Always +... diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 49e1a81c..00000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "rocketlib"] - path = rocketlib - url = https://github.com/waterloo-rocketry/rocketlib.git diff --git a/Makefile b/Makefile index 75f40d4d..3eb63585 100644 --- a/Makefile +++ b/Makefile @@ -1,43 +1,61 @@ COMMON_C_SRCS := \ - message/msg_actuator.c \ - message/msg_canards.c \ - message/msg_general.c \ - message/msg_gps.c \ - message/msg_recovery.c \ - message/msg_sensor.c \ - message/msg_stream.c \ - message/msg_telemetry.c \ - util/can_rcv_buffer.c \ - util/can_tx_buffer.c \ - util/safe_ring_buffer.c \ - util/timing_util.c + can_buffer/can_rcv_buffer.c \ + can_buffer/can_tx_buffer.c \ + can_buffer/safe_ring_buffer.c \ + can_message/msg_actuator.c \ + can_message/msg_canards.c \ + can_message/msg_general.c \ + can_message/msg_gps.c \ + can_message/msg_recovery.c \ + can_message/msg_sensor.c \ + can_message/msg_stream.c \ + can_message/msg_telemetry.c \ + common/crc8.c \ + common/low_pass_filter.c \ + logging/mbr.c \ + pic18f26k83/pic18f26k83_can_timing_util.c +# rocketlib.h is intentional not included to avoid clang-tidy misc-include-cleaner error COMMON_C_HEADERS := \ - message/msg_actuator.h \ - message/msg_canards.h \ - message/msg_common.h \ - message/msg_general.h \ - message/msg_gps.h \ - message/msg_recovery.h \ - message/msg_sensor.h \ - message/msg_stream.h \ - message/msg_telemetry.h \ - util/can_rcv_buffer.h \ - util/can_tx_buffer.h \ - util/safe_ring_buffer.h \ - util/timing_util.h - -PIC18_C_SRCS := pic18f26k83/pic18f26k83_can.c - -PIC18_C_HEADERS := pic18f26k83/pic18f26k83_can.h + can_buffer/can_rcv_buffer.h \ + can_buffer/can_tx_buffer.h \ + can_buffer/safe_ring_buffer.h \ + can_message/msg_actuator.h \ + can_message/msg_canards.h \ + can_message/msg_common.h \ + can_message/msg_general.h \ + can_message/msg_gps.h \ + can_message/msg_recovery.h \ + can_message/msg_sensor.h \ + can_message/msg_stream.h \ + can_message/msg_telemetry.h \ + common/common.h \ + common/crc8.h \ + common/electrical.h \ + common/low_pass_filter.h \ + common/mathops.h \ + logging/mbr.h \ + pic18f26k83/pic18f26k83_can_timing_util.h + +PIC18_C_SRCS := \ + pic18f26k83/pic18f26k83_can.c \ + pic18f26k83/pic18f26k83_timer.c + +PIC18_C_HEADERS := \ + pic18f26k83/pic18f26k83_can.h \ + pic18f26k83/pic18f26k83_timer.h STM32H7_C_SRCS := stm32h7/stm32h7_can.c STM32H7_C_HEADERS := stm32h7/stm32h7_can.h -INCLUDE_PATHS := . rocketlib/include +INCLUDE_PATHS := . TEST_SRCS := \ + tests/test_crc8.cpp \ + tests/test_low_pass_filter.cpp \ + tests/test_mathops.cpp \ + tests/test_mbr.cpp \ tests/test_msg_actuator.cpp \ tests/test_msg_canards.cpp \ tests/test_msg_common.cpp \ @@ -48,11 +66,12 @@ TEST_SRCS := \ tests/test_msg_stream.cpp \ tests/test_msg_telemetry.cpp \ tests/test_nullptr_reject.cpp \ + tests/test_rockettest.cpp \ tests/test_timing_util.cpp \ tests/test_tx_rcv_buffer.cpp -ROCKETLIB_SUBMODULE_PATH := rocketlib +ROCKETLIB_SUBMODULE_PATH := . EXTRA_C_CXX_FLAGS := -DBOARD_TYPE_UNIQUE_ID=BOARD_TYPE_ID_ARMING -DBOARD_INST_UNIQUE_ID=BOARD_INST_ID_ROCKET -include rocketlib/flows/firmware-library.mk +include flows/firmware-library.mk diff --git a/can.h b/can.h deleted file mode 100644 index d680c7e3..00000000 --- a/can.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * file that defines structures that are common across all 3 of - * Waterloo Rocketry's CAN implementations - */ - -#ifndef CANLIB_CAN_H -#define CANLIB_CAN_H - -#include -#include - -#define CANLIB_BIT_TIME_US 4 - -typedef uint32_t can_sid_t; - -// Timing parameters -typedef struct { - // BaudRate Prescaler - uint8_t brp; - // Synchronization Jump Width - uint8_t sjw; - - // sample once or three times - uint8_t sam; - // phase segment 1 bits - uint8_t seg1ph; - // phase segment 2 bits - uint8_t seg2ph; - // propagation time segment bits - uint8_t prseg; - - // Phase segment 2 time select bit. If true, then use seg2ph, - // otherwise take minimum viable phase length - bool btlmode; -} can_timing_t; - -// Things that are in a CAN message -typedef struct { - // Standard Identifier - 29 bits long - can_sid_t sid; - // How many bytes are used in data - uint8_t data_len; - // the data you want to transmit - uint8_t data[8]; -} can_msg_t; - -#endif // CAN_H_ diff --git a/util/can_rcv_buffer.c b/can_buffer/can_rcv_buffer.c similarity index 85% rename from util/can_rcv_buffer.c rename to can_buffer/can_rcv_buffer.c index 2125f70c..b9d9e65f 100644 --- a/util/can_rcv_buffer.c +++ b/can_buffer/can_rcv_buffer.c @@ -1,10 +1,10 @@ #include -#include "common.h" +#include "common/common.h" -#include "can.h" -#include "can_rcv_buffer.h" -#include "safe_ring_buffer.h" +#include "can_buffer/can_rcv_buffer.h" +#include "can_buffer/safe_ring_buffer.h" +#include "can_message/can.h" static srb_ctx_t buf; static bool overflow_flag; diff --git a/util/can_rcv_buffer.h b/can_buffer/can_rcv_buffer.h similarity index 96% rename from util/can_rcv_buffer.h rename to can_buffer/can_rcv_buffer.h index f1f831b8..59036bf8 100644 --- a/util/can_rcv_buffer.h +++ b/can_buffer/can_rcv_buffer.h @@ -1,5 +1,5 @@ -#ifndef CANLIB_CAN_RCV_BUFFER_H -#define CANLIB_CAN_RCV_BUFFER_H +#ifndef ROCKETLIB_CAN_RCV_BUFFER_H +#define ROCKETLIB_CAN_RCV_BUFFER_H /** * @file can_rcv_buffer.h @@ -24,9 +24,9 @@ #include #include -#include "common.h" +#include "common/common.h" -#include "can.h" +#include "can_message/can.h" #ifdef __cplusplus extern "C" { diff --git a/util/can_tx_buffer.c b/can_buffer/can_tx_buffer.c similarity index 86% rename from util/can_tx_buffer.c rename to can_buffer/can_tx_buffer.c index 2e6ec58e..9a30c61e 100644 --- a/util/can_tx_buffer.c +++ b/can_buffer/can_tx_buffer.c @@ -1,10 +1,10 @@ #include -#include "common.h" +#include "common/common.h" -#include "can.h" -#include "can_tx_buffer.h" -#include "safe_ring_buffer.h" +#include "can_buffer/can_tx_buffer.h" +#include "can_buffer/safe_ring_buffer.h" +#include "can_message/can.h" typedef struct { void (*can_send_fp)(const can_msg_t *); diff --git a/util/can_tx_buffer.h b/can_buffer/can_tx_buffer.h similarity index 89% rename from util/can_tx_buffer.h rename to can_buffer/can_tx_buffer.h index df64ffe0..b67dbee3 100644 --- a/util/can_tx_buffer.h +++ b/can_buffer/can_tx_buffer.h @@ -1,12 +1,12 @@ -#ifndef CANLIB_CAN_TX_BUFFER_H -#define CANLIB_CAN_TX_BUFFER_H +#ifndef ROCKETLIB_CAN_TX_BUFFER_H +#define ROCKETLIB_CAN_TX_BUFFER_H #include #include -#include "common.h" +#include "common/common.h" -#include "can.h" +#include "can_message/can.h" #ifdef __cplusplus extern "C" { diff --git a/util/safe_ring_buffer.c b/can_buffer/safe_ring_buffer.c similarity index 96% rename from util/safe_ring_buffer.c rename to can_buffer/safe_ring_buffer.c index 7efab72b..367515c9 100644 --- a/util/safe_ring_buffer.c +++ b/can_buffer/safe_ring_buffer.c @@ -1,9 +1,9 @@ #include #include -#include "common.h" +#include "common/common.h" -#include "safe_ring_buffer.h" +#include "can_buffer/safe_ring_buffer.h" static size_t get_offset_bytes(const srb_ctx_t *ctx, size_t index) { w_assert(ctx); diff --git a/util/safe_ring_buffer.h b/can_buffer/safe_ring_buffer.h similarity index 95% rename from util/safe_ring_buffer.h rename to can_buffer/safe_ring_buffer.h index fa328c7d..1d966f66 100644 --- a/util/safe_ring_buffer.h +++ b/can_buffer/safe_ring_buffer.h @@ -1,10 +1,10 @@ -#ifndef CANLIB_SAFE_RING_BUFFER_H -#define CANLIB_SAFE_RING_BUFFER_H +#ifndef ROCKETLIB_SAFE_RING_BUFFER_H +#define ROCKETLIB_SAFE_RING_BUFFER_H #include #include -#include "common.h" +#include "common/common.h" /* * Context variable for the module. This allows you to initialize multiple diff --git a/can_message/can.h b/can_message/can.h new file mode 100644 index 00000000..68930a4d --- /dev/null +++ b/can_message/can.h @@ -0,0 +1,18 @@ +#ifndef ROCKETLIB_CAN_H +#define ROCKETLIB_CAN_H + +#include +#include + +typedef uint32_t can_sid_t; + +typedef struct { + // Standard Identifier - 29 bits long + can_sid_t sid; + // How many bytes are used in data + uint8_t data_len; + // the data you want to transmit + uint8_t data[8]; +} can_msg_t; + +#endif diff --git a/message/msg_actuator.c b/can_message/msg_actuator.c similarity index 93% rename from message/msg_actuator.c rename to can_message/msg_actuator.c index 9af719ff..54b45fd0 100644 --- a/message/msg_actuator.c +++ b/can_message/msg_actuator.c @@ -1,12 +1,12 @@ #include #include -#include "common.h" +#include "common/common.h" -#include "can.h" -#include "message_types.h" -#include "msg_actuator.h" -#include "msg_common.h" +#include "can_message/can.h" +#include "can_message/msg_actuator.h" +#include "can_message/msg_common.h" +#include "can_message/packet_format.h" void build_actuator_cmd_msg(can_msg_prio_t prio, uint16_t timestamp, can_actuator_id_t actuator_id, can_actuator_state_t actuator_cmd, can_msg_t *output) { diff --git a/message/msg_actuator.h b/can_message/msg_actuator.h similarity index 88% rename from message/msg_actuator.h rename to can_message/msg_actuator.h index 075dc854..3ec5ec8a 100644 --- a/message/msg_actuator.h +++ b/can_message/msg_actuator.h @@ -1,13 +1,13 @@ -#ifndef CANLIB_ACTUATOR_H -#define CANLIB_ACTUATOR_H +#ifndef ROCKETLIB_ACTUATOR_H +#define ROCKETLIB_ACTUATOR_H #include #include -#include "common.h" +#include "common/common.h" -#include "can.h" -#include "message_types.h" +#include "can_message/can.h" +#include "can_message/packet_format.h" #ifdef __cplusplus extern "C" { diff --git a/message/msg_canards.c b/can_message/msg_canards.c similarity index 89% rename from message/msg_canards.c rename to can_message/msg_canards.c index b0ba6630..a92252d6 100644 --- a/message/msg_canards.c +++ b/can_message/msg_canards.c @@ -1,12 +1,12 @@ #include #include -#include "common.h" +#include "common/common.h" -#include "can.h" -#include "message_types.h" -#include "msg_canards.h" -#include "msg_common.h" +#include "can_message/can.h" +#include "can_message/msg_canards.h" +#include "can_message/msg_common.h" +#include "can_message/packet_format.h" void build_canard_firmware_error_msg(can_msg_prio_t prio, uint16_t timestamp, can_canards_module_id_t module_id, uint32_t error_bitfield, diff --git a/message/msg_canards.h b/can_message/msg_canards.h similarity index 89% rename from message/msg_canards.h rename to can_message/msg_canards.h index 3b1a457d..2f9e156b 100644 --- a/message/msg_canards.h +++ b/can_message/msg_canards.h @@ -1,13 +1,13 @@ -#ifndef CANLIB_CANARDS_H -#define CANLIB_CANARDS_H +#ifndef ROCKETLIB_CANARDS_H +#define ROCKETLIB_CANARDS_H #include #include -#include "common.h" +#include "common/common.h" -#include "can.h" -#include "message_types.h" +#include "can_message/can.h" +#include "can_message/packet_format.h" #ifdef __cplusplus extern "C" { diff --git a/message/msg_common.h b/can_message/msg_common.h similarity index 92% rename from message/msg_common.h rename to can_message/msg_common.h index 4c115735..3c148afc 100644 --- a/message/msg_common.h +++ b/can_message/msg_common.h @@ -1,15 +1,15 @@ -#ifndef CANLIB_COMMON_H -#define CANLIB_COMMON_H +#ifndef ROCKETLIB_MSG_COMMON_H +#define ROCKETLIB_MSG_COMMON_H #include #include -#include "can.h" -#include "message_types.h" +#include "can_message/can.h" +#include "can_message/packet_format.h" -#include "common.h" +#include "common/common.h" -#ifndef CANLIB_DYNAMIC_BOARD_ID +#ifndef CAN_DYNAMIC_BOARD_ID #ifndef BOARD_TYPE_UNIQUE_ID #warning BOARD_TYPE_UNIQUE_ID not defined, please set that up in project @@ -91,7 +91,7 @@ static inline uint16_t get_timestamp(const can_msg_t *msg) { } // Dynamic board ID specific function -#ifdef CANLIB_DYNAMIC_BOARD_ID +#ifdef CAN_DYNAMIC_BOARD_ID static inline void set_board_id(uint8_t board_type_id, uint8_t board_inst_id) { w_assert(((uint32_t)board_type_id & 0xc0) == 0); diff --git a/message/msg_general.c b/can_message/msg_general.c similarity index 96% rename from message/msg_general.c rename to can_message/msg_general.c index 486d34cd..8bf24e70 100644 --- a/message/msg_general.c +++ b/can_message/msg_general.c @@ -2,12 +2,12 @@ #include #include -#include "common.h" +#include "common/common.h" -#include "can.h" -#include "message_types.h" -#include "msg_common.h" -#include "msg_general.h" +#include "can_message/can.h" +#include "can_message/msg_common.h" +#include "can_message/msg_general.h" +#include "can_message/packet_format.h" void build_general_board_status_msg(can_msg_prio_t prio, uint16_t timestamp, uint32_t board_error_bitfield, can_msg_t *output) { @@ -128,7 +128,7 @@ w_status_t check_board_need_reset(const can_msg_t *msg, bool *board_need_reset) return status; } -#ifndef CANLIB_DYNAMIC_BOARD_ID +#ifndef CAN_DYNAMIC_BOARD_ID const uint8_t board_type_unique_id = BOARD_TYPE_UNIQUE_ID; const uint8_t board_inst_unique_id = BOARD_INST_UNIQUE_ID; #endif diff --git a/message/msg_general.h b/can_message/msg_general.h similarity index 92% rename from message/msg_general.h rename to can_message/msg_general.h index 6c6c2bfd..213405cb 100644 --- a/message/msg_general.h +++ b/can_message/msg_general.h @@ -1,13 +1,13 @@ -#ifndef CANLIB_GENERAL_H -#define CANLIB_GENERAL_H +#ifndef ROCKETLIB_GENERAL_H +#define ROCKETLIB_GENERAL_H #include #include -#include "common.h" +#include "common/common.h" -#include "can.h" -#include "message_types.h" +#include "can_message/can.h" +#include "can_message/packet_format.h" #ifdef __cplusplus extern "C" { diff --git a/message/msg_gps.c b/can_message/msg_gps.c similarity index 96% rename from message/msg_gps.c rename to can_message/msg_gps.c index 1fd69f70..c7844ad0 100644 --- a/message/msg_gps.c +++ b/can_message/msg_gps.c @@ -1,12 +1,12 @@ #include #include -#include "common.h" +#include "common/common.h" -#include "can.h" -#include "message_types.h" -#include "msg_common.h" -#include "msg_gps.h" +#include "can_message/can.h" +#include "can_message/msg_common.h" +#include "can_message/msg_gps.h" +#include "can_message/packet_format.h" void build_gps_time_msg(can_msg_prio_t prio, uint16_t timestamp, uint8_t utc_hours, uint8_t utc_mins, uint8_t utc_secs, uint8_t utc_dsecs, can_msg_t *output) { diff --git a/message/msg_gps.h b/can_message/msg_gps.h similarity index 95% rename from message/msg_gps.h rename to can_message/msg_gps.h index ff269511..8e70fca2 100644 --- a/message/msg_gps.h +++ b/can_message/msg_gps.h @@ -1,13 +1,13 @@ -#ifndef CANLIB_GPS_H -#define CANLIB_GPS_H +#ifndef ROCKETLIB_GPS_H +#define ROCKETLIB_GPS_H #include #include -#include "common.h" +#include "common/common.h" -#include "can.h" -#include "message_types.h" +#include "can_message/can.h" +#include "can_message/packet_format.h" #ifdef __cplusplus extern "C" { diff --git a/message/msg_recovery.c b/can_message/msg_recovery.c similarity index 92% rename from message/msg_recovery.c rename to can_message/msg_recovery.c index c68ff039..9322f8e7 100644 --- a/message/msg_recovery.c +++ b/can_message/msg_recovery.c @@ -1,11 +1,11 @@ #include -#include "common.h" +#include "common/common.h" -#include "can.h" -#include "message_types.h" -#include "msg_common.h" -#include "msg_recovery.h" +#include "can_message/can.h" +#include "can_message/msg_common.h" +#include "can_message/msg_recovery.h" +#include "can_message/packet_format.h" void build_alt_arm_cmd_msg(can_msg_prio_t prio, uint16_t timestamp, can_altimeter_id_t alt_id, can_alt_arm_state_t arm_cmd, can_msg_t *output) { diff --git a/message/msg_recovery.h b/can_message/msg_recovery.h similarity index 88% rename from message/msg_recovery.h rename to can_message/msg_recovery.h index 46182ba5..2fcf2309 100644 --- a/message/msg_recovery.h +++ b/can_message/msg_recovery.h @@ -1,13 +1,13 @@ -#ifndef CANLIB_RECOVERY_H -#define CANLIB_RECOVERY_H +#ifndef ROCKETLIB_RECOVERY_H +#define ROCKETLIB_RECOVERY_H #include #include -#include "common.h" +#include "common/common.h" -#include "can.h" -#include "message_types.h" +#include "can_message/can.h" +#include "can_message/packet_format.h" #ifdef __cplusplus extern "C" { diff --git a/message/msg_sensor.c b/can_message/msg_sensor.c similarity index 97% rename from message/msg_sensor.c rename to can_message/msg_sensor.c index 94a68f2d..cb23d60c 100644 --- a/message/msg_sensor.c +++ b/can_message/msg_sensor.c @@ -1,12 +1,12 @@ #include #include -#include "common.h" +#include "common/common.h" -#include "can.h" -#include "message_types.h" -#include "msg_common.h" -#include "msg_sensor.h" +#include "can_message/can.h" +#include "can_message/msg_common.h" +#include "can_message/msg_sensor.h" +#include "can_message/packet_format.h" void build_analog_sensor_16bit_msg(can_msg_prio_t prio, uint16_t timestamp, can_analog_sensor_id_t sensor_id, uint16_t sensor_data, diff --git a/message/msg_sensor.h b/can_message/msg_sensor.h similarity index 95% rename from message/msg_sensor.h rename to can_message/msg_sensor.h index b5700595..58131552 100644 --- a/message/msg_sensor.h +++ b/can_message/msg_sensor.h @@ -1,13 +1,13 @@ -#ifndef CANLIB_SENSOR_H -#define CANLIB_SENSOR_H +#ifndef ROCKETLIB_SENSOR_H +#define ROCKETLIB_SENSOR_H #include #include -#include "common.h" +#include "common/common.h" -#include "can.h" -#include "message_types.h" +#include "can_message/can.h" +#include "can_message/packet_format.h" #ifdef __cplusplus extern "C" { diff --git a/message/msg_stream.c b/can_message/msg_stream.c similarity index 94% rename from message/msg_stream.c rename to can_message/msg_stream.c index 02c9f9c4..cc45e82a 100644 --- a/message/msg_stream.c +++ b/can_message/msg_stream.c @@ -1,12 +1,12 @@ #include #include -#include "common.h" +#include "common/common.h" -#include "can.h" -#include "message_types.h" -#include "msg_common.h" -#include "msg_stream.h" +#include "can_message/can.h" +#include "can_message/msg_common.h" +#include "can_message/msg_stream.h" +#include "can_message/packet_format.h" // Fields are 24-bit big-endian. #define STREAM_SIZE_MAX 0xffffffU diff --git a/message/msg_stream.h b/can_message/msg_stream.h similarity index 87% rename from message/msg_stream.h rename to can_message/msg_stream.h index 5f74464e..911d70bd 100644 --- a/message/msg_stream.h +++ b/can_message/msg_stream.h @@ -1,13 +1,13 @@ -#ifndef CANLIB_STREAM_H -#define CANLIB_STREAM_H +#ifndef ROCKETLIB_STREAM_H +#define ROCKETLIB_STREAM_H #include #include -#include "common.h" +#include "common/common.h" -#include "can.h" -#include "message_types.h" +#include "can_message/can.h" +#include "can_message/packet_format.h" #ifdef __cplusplus extern "C" { diff --git a/message/msg_telemetry.c b/can_message/msg_telemetry.c similarity index 90% rename from message/msg_telemetry.c rename to can_message/msg_telemetry.c index 136e4b39..d9a27f57 100644 --- a/message/msg_telemetry.c +++ b/can_message/msg_telemetry.c @@ -1,12 +1,12 @@ #include #include -#include "common.h" +#include "common/common.h" -#include "can.h" -#include "message_types.h" -#include "msg_common.h" -#include "msg_telemetry.h" +#include "can_message/can.h" +#include "can_message/msg_common.h" +#include "can_message/msg_telemetry.h" +#include "can_message/packet_format.h" void build_telemetry_info_msg(can_msg_prio_t prio, uint16_t timestamp, uint8_t channel_id, uint8_t lqi, int8_t rssi, can_msg_t *output) { diff --git a/message/msg_telemetry.h b/can_message/msg_telemetry.h similarity index 92% rename from message/msg_telemetry.h rename to can_message/msg_telemetry.h index f710396b..c9ba0030 100644 --- a/message/msg_telemetry.h +++ b/can_message/msg_telemetry.h @@ -1,13 +1,13 @@ -#ifndef CANLIB_TELEMETRY_H -#define CANLIB_TELEMETRY_H +#ifndef ROCKETLIB_TELEMETRY_H +#define ROCKETLIB_TELEMETRY_H #include #include -#include "common.h" +#include "common/common.h" -#include "can.h" -#include "message_types.h" +#include "can_message/can.h" +#include "can_message/packet_format.h" #ifdef __cplusplus extern "C" { diff --git a/message_types.h b/can_message/packet_format.h similarity index 99% rename from message_types.h rename to can_message/packet_format.h index 519cbba1..10c1a310 100644 --- a/message_types.h +++ b/can_message/packet_format.h @@ -1,7 +1,7 @@ // Auto generated file, do not edit directly -#ifndef CANLIB_MESSAGE_TYPES_H -#define CANLIB_MESSAGE_TYPES_H +#ifndef ROCKETLIB_PACKET_FORMAT_H +#define ROCKETLIB_PACKET_FORMAT_H // Message Priority typedef enum { diff --git a/canlib.h b/canlib.h deleted file mode 100644 index 20eff29b..00000000 --- a/canlib.h +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef CANLIB_CANLIB_H -#define CANLIB_CANLIB_H - -// NOLINTBEGIN - -#include "can.h" -#include "message_types.h" - -#include "message/msg_actuator.h" -#include "message/msg_canards.h" -#include "message/msg_common.h" -#include "message/msg_general.h" -#include "message/msg_gps.h" -#include "message/msg_recovery.h" -#include "message/msg_sensor.h" -#include "message/msg_stream.h" -#include "message/msg_telemetry.h" - -#include "util/can_rcv_buffer.h" -#include "util/can_tx_buffer.h" -#include "util/timing_util.h" - -#if (defined(STM32H723xx) || defined(STM32H733xx) || defined(STM32H750xx) || defined(STM32H753xx)) - -#include "stm32h7/stm32h7_can.h" - -#elif ConfName == PIC18F26K83 - -#include "pic18f26k83/pic18f26k83_can.h" - -#elif ConfName == MCP2515 - -#include "mcp2515/mcp_2515.h" - -#elif ConfName == DSPIC33EPXXXGP50X - -#include "dspic33epxxxgp50x/dspic33epxxxgp50x_can.h" - -#else - -#error "canlib project configuration not set." - -#endif - -// NOLINTEND - -#endif diff --git a/common/common.h b/common/common.h new file mode 100644 index 00000000..7ce0eee4 --- /dev/null +++ b/common/common.h @@ -0,0 +1,123 @@ +#ifndef ROCKETLIB_COMMON_H +#define ROCKETLIB_COMMON_H + +/// @brief Rocketlib major release number, shall be the design cycle year +#define ROCKETLIB_VERSION_MAJOR 2027 + +/// @brief Rocketlib minor release number, shall be the RocketCAN protocol version +#define ROCKETLIB_VERSION_MINOR 1 + +/// @brief Rocketlib patch release number, shall be the n-th revision of this library that does not +/// involve RocketCAN protocol change +#define ROCKETLIB_VERSION_PATCH 0 + +typedef float float32_t; +typedef double float64_t; + +#ifdef __cplusplus +#define STATIC_ASSERT(expr, msg) static_assert((expr), msg); +#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +#define STATIC_ASSERT(expr, msg) _Static_assert((expr), msg); +#else +#define STATIC_ASSERT(expr, msg) +#endif + +STATIC_ASSERT(sizeof(float32_t) == 4, "float32_t must be 32 bits") +STATIC_ASSERT(sizeof(float64_t) == 8, "float64_t must be 64 bits") + +/// @brief Standard error code +typedef enum { + /// @brief Success + W_SUCCESS = 0, + /// @brief Failure, if not one of the more specific case below + W_FAILURE, + /// @brief Illegal function call arguments/parameters + W_INVALID_PARAM, + /// @brief IO error, for example no SD card was plugged in + W_IO_ERROR, + /// @brief IO timeout, for example SD card takes too long to respond + W_IO_TIMEOUT, + /// @brief Math error, for example divide by zero + W_MATH_ERROR, + /// @brief Buffer overflow, for example trying to write/push to a full FIFO buffer + W_OVERFLOW, + /// @brief Buffer underflow, for example trying to read/pop from an empty FIFO buffer + W_UNDERFLOW, + /// @brief Data format error, for example input is a malformed CAN packet + W_DATA_FORMAT_ERROR, + /// @brief Succeed, but no data available + W_NO_DATA +} w_status_t; + +#ifndef UNIT_TEST + +// Assertion Macro +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Assertion fail handler + * + * Assertion fail handler, shall be implemented in the firmware, only used if `W_DEBUG` is defined + * + * @param file Path to C source file contains assertion failure + * @param line C source line number of assertion failure + * @param statement Statement caused assertion failure + */ +void w_assert_fail(const char *file, int line, const char *statement); + +#ifdef __cplusplus +} +#endif + +#ifdef W_DEBUG + +#define w_assert(statement) \ + if (!(statement)) { \ + w_assert_fail(__FILE__, __LINE__, #statement); \ + } + +#else + +/** + * @brief Assertion + * + * Works just like standard C assertion, except use can define custom assertion failure handler(see + * `w_assert_fail`), only active when `W_DEBUG` is defined + */ +#define w_assert(statement) + +#endif + +#else + +#ifdef __cplusplus + +extern void rocketlib_assert_pass_cpp(const char *file, int line, const char *statement); +extern void rocketlib_assert_fail_cpp(const char *file, int line, const char *statement); + +#define w_assert(statement) \ + if (statement) { \ + rocketlib_assert_pass_cpp(__FILE__, __LINE__, #statement); \ + } else { \ + rocketlib_assert_fail_cpp(__FILE__, __LINE__, #statement); \ + } + +#else + +extern void rocketlib_assert_pass_c(const char *file, int line, const char *statement); +extern void rocketlib_assert_fail_c(const char *file, int line, const char *statement); + +#define w_assert(statement) \ + if (statement) { \ + rocketlib_assert_pass_c(__FILE__, __LINE__, #statement); \ + } else { \ + rocketlib_assert_fail_c(__FILE__, __LINE__, #statement); \ + } + +#endif + +#endif + +#endif diff --git a/common/crc8.c b/common/crc8.c new file mode 100644 index 00000000..e6ca2ec9 --- /dev/null +++ b/common/crc8.c @@ -0,0 +1,34 @@ +#include +#include + +#include "common/common.h" + +// crc table used for calculation +static uint8_t table[256] = { + 0x00, 0x07, 0x0e, 0x09, 0x1c, 0x1b, 0x12, 0x15, 0x38, 0x3f, 0x36, 0x31, 0x24, 0x23, 0x2a, 0x2d, + 0x70, 0x77, 0x7e, 0x79, 0x6c, 0x6b, 0x62, 0x65, 0x48, 0x4f, 0x46, 0x41, 0x54, 0x53, 0x5a, 0x5d, + 0xe0, 0xe7, 0xee, 0xe9, 0xfc, 0xfb, 0xf2, 0xf5, 0xd8, 0xdf, 0xd6, 0xd1, 0xc4, 0xc3, 0xca, 0xcd, + 0x90, 0x97, 0x9e, 0x99, 0x8c, 0x8b, 0x82, 0x85, 0xa8, 0xaf, 0xa6, 0xa1, 0xb4, 0xb3, 0xba, 0xbd, + 0xc7, 0xc0, 0xc9, 0xce, 0xdb, 0xdc, 0xd5, 0xd2, 0xff, 0xf8, 0xf1, 0xf6, 0xe3, 0xe4, 0xed, 0xea, + 0xb7, 0xb0, 0xb9, 0xbe, 0xab, 0xac, 0xa5, 0xa2, 0x8f, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9d, 0x9a, + 0x27, 0x20, 0x29, 0x2e, 0x3b, 0x3c, 0x35, 0x32, 0x1f, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0d, 0x0a, + 0x57, 0x50, 0x59, 0x5e, 0x4b, 0x4c, 0x45, 0x42, 0x6f, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7d, 0x7a, + 0x89, 0x8e, 0x87, 0x80, 0x95, 0x92, 0x9b, 0x9c, 0xb1, 0xb6, 0xbf, 0xb8, 0xad, 0xaa, 0xa3, 0xa4, + 0xf9, 0xfe, 0xf7, 0xf0, 0xe5, 0xe2, 0xeb, 0xec, 0xc1, 0xc6, 0xcf, 0xc8, 0xdd, 0xda, 0xd3, 0xd4, + 0x69, 0x6e, 0x67, 0x60, 0x75, 0x72, 0x7b, 0x7c, 0x51, 0x56, 0x5f, 0x58, 0x4d, 0x4a, 0x43, 0x44, + 0x19, 0x1e, 0x17, 0x10, 0x05, 0x02, 0x0b, 0x0c, 0x21, 0x26, 0x2f, 0x28, 0x3d, 0x3a, 0x33, 0x34, + 0x4e, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5c, 0x5b, 0x76, 0x71, 0x78, 0x7f, 0x6a, 0x6d, 0x64, 0x63, + 0x3e, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2c, 0x2b, 0x06, 0x01, 0x08, 0x0f, 0x1a, 0x1d, 0x14, 0x13, + 0xae, 0xa9, 0xa0, 0xa7, 0xb2, 0xb5, 0xbc, 0xbb, 0x96, 0x91, 0x98, 0x9f, 0x8a, 0x8d, 0x84, 0x83, + 0xde, 0xd9, 0xd0, 0xd7, 0xc2, 0xc5, 0xcc, 0xcb, 0xe6, 0xe1, 0xe8, 0xef, 0xfa, 0xfd, 0xf4, 0xf3 +}; + +// NOLINTNEXTLINE(misc-use-internal-linkage) +uint8_t crc8_checksum(const uint8_t *pdata, size_t nbytes, uint8_t crc) { + w_assert(pdata); + while (nbytes > 0) { + crc = table[(crc ^ *pdata++) & 0xff]; + --nbytes; + } + return crc; +} diff --git a/common/crc8.h b/common/crc8.h new file mode 100644 index 00000000..03c02523 --- /dev/null +++ b/common/crc8.h @@ -0,0 +1,28 @@ +#ifndef ROCKETLIB_CRC8_H +#define ROCKETLIB_CRC8_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Compute CRC8 + * + * Computes the CRC8 checksum of input data + * + * @param pdata Input data buffer + * @param nbytes Buffer size in bytes + * @param crc CRC8 checksum of previous CRC8 calculation if calculating checksum on multiple + * discontinuous buffers, otherwise 0 + * @return Computed CRC8 checksum + */ +uint8_t crc8_checksum(const uint8_t *pdata, size_t nbytes, uint8_t crc); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/common/electrical.h b/common/electrical.h new file mode 100644 index 00000000..25ca35a9 --- /dev/null +++ b/common/electrical.h @@ -0,0 +1,11 @@ +#ifndef ROCKETLIB_ELECTRICAL_H +#define ROCKETLIB_ELECTRICAL_H + +// 12V Rail Powered by 3S LiPo +#define RAIL_12V_MAX_mV 12800 +#define RAIL_12V_MIN_mV 11600 + +// INA180 Current Sense Amplifier +#define INA180A3_GAIN 100 + +#endif diff --git a/common/low_pass_filter.c b/common/low_pass_filter.c new file mode 100644 index 00000000..a41ec9b6 --- /dev/null +++ b/common/low_pass_filter.c @@ -0,0 +1,54 @@ +#include +#include + +#include "common/common.h" +#include "common/low_pass_filter.h" + +/** + * @brief Initializes the low-pass filter by calculating and storing the alpha value + * + * This function calculates the alpha value based on the given response time and stores + * it in the provided pointer. The alpha value is used in subsequent filter updates. + * + * @param alpha Pointer to store the calculated alpha value + * @param response_time Response time constant in milliseconds (must be > 0) + * @return w_status_t Returns W_SUCCESS on success, W_INVALID_PARAM if alpha is NULL or + * response_time is invalid + */ +w_status_t low_pass_filter_init(double *alpha, double response_time) { + if (alpha == NULL || response_time <= 0) { + return W_INVALID_PARAM; // Return specific error for invalid parameters + } + // Calculate and store the alpha value using the low_pass_alpha function + *alpha = low_pass_alpha(response_time, response_time); + return W_SUCCESS; // Return success after initialization +} + +/** + * @brief Updates the low-pass filter with a new value and returns the operation status + * + * This function applies the low-pass filter algorithm to the new input value using the + * provided alpha value. The filtered result is stored in low_pass_value and updated + * in-place for the next iteration. + * + * @param alpha Alpha value (0.0 to 1.0) for the low-pass filter + * @param new_input_value New input value to filter + * @param low_pass_value Pointer to the current filtered value (updated in-place) + * @return w_status_t Returns W_SUCCESS on success, W_INVALID_PARAM if low_pass_value is + * NULL or alpha is out of valid range (0.0 to 1.0) + */ +w_status_t update_low_pass(double alpha, uint16_t new_input_value, double *low_pass_value) { + // Check if low_pass_value pointer is NULL + if (low_pass_value == NULL) { + return W_INVALID_PARAM; // Return specific error for invalid parameters + } + + // Ensure alpha is within the valid range + if (alpha <= 0.0 || alpha >= 1.0) { + return W_INVALID_PARAM; + } + + // Low pass filter formula: y[n] = alpha * x[n] + (1 - alpha) * y[n-1] + *low_pass_value = (alpha * new_input_value) + ((1.0 - alpha) * (*low_pass_value)); + return W_SUCCESS; // Return success after successful update +} diff --git a/common/low_pass_filter.h b/common/low_pass_filter.h new file mode 100644 index 00000000..d0cadcfc --- /dev/null +++ b/common/low_pass_filter.h @@ -0,0 +1,80 @@ +/** + * @file + * @brief Low-pass filter implementation + * + * This module provides a low-pass filter implementation for smoothing sensor readings + * and reducing noise in analog signals. The filter uses an exponential moving average + * algorithm with configurable response time. + */ + +#ifndef ROCKETLIB_LOW_PASS_FILTER_H +#define ROCKETLIB_LOW_PASS_FILTER_H + +#include +#include + +#include "common/common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Calculate the sample frequency based on the time difference in milliseconds + * + * @param time_diff_ms Time difference between samples in milliseconds + * @return double Sample frequency in Hz + */ +static inline double sample_freq(double time_diff_ms) { + return 1000.0 / time_diff_ms; +} + +/** + * @brief Calculate the alpha value for the low-pass filter based on the response time + * + * The alpha value determines the filter's responsiveness. A higher alpha value results + * in faster response but less filtering, while a lower alpha value provides more filtering + * but slower response. + * + * @param TR Response time constant in milliseconds + * @param time_diff_ms Time difference between samples in milliseconds + * @return double Alpha value (0.0 to 1.0) for the low-pass filter + */ +static inline double low_pass_alpha(double TR, double time_diff_ms) { + double freq = sample_freq(time_diff_ms); + return (freq * TR / 5.0) / (1.0 + freq * TR / 5.0); +} + +/** + * @brief Initializes the low-pass filter by calculating and storing the alpha value + * + * This function calculates the alpha value based on the given response time and stores + * it in the provided pointer. The alpha value is used in subsequent filter updates. + * + * @param alpha Pointer to store the calculated alpha value + * @param response_time Response time constant in milliseconds (must be > 0) + * @return w_status_t Returns W_SUCCESS on success, W_INVALID_PARAM if alpha is NULL or + * response_time is invalid + */ +w_status_t low_pass_filter_init(double *alpha, double response_time); + +/** + * @brief Updates the low-pass filter with a new value and returns the operation status + * + * This function applies the low-pass filter algorithm to the new input value using the + * provided alpha value. The filtered result is stored in low_pass_value and updated + * in-place for the next iteration. + * + * @param alpha Alpha value (0.0 to 1.0) for the low-pass filter + * @param new_input_value New input value to filter + * @param low_pass_value Pointer to the current filtered value (updated in-place) + * @return w_status_t Returns W_SUCCESS on success, W_INVALID_PARAM if low_pass_value is + * NULL or alpha is out of valid range (0.0 to 1.0) + */ +w_status_t update_low_pass(double alpha, uint16_t new_input_value, double *low_pass_value); + +#ifdef __cplusplus +} +#endif + +#endif /* ROCKETLIB_LOW_PASS_FILTER_H */ diff --git a/common/mathops.h b/common/mathops.h new file mode 100644 index 00000000..b629cbc3 --- /dev/null +++ b/common/mathops.h @@ -0,0 +1,62 @@ +#ifndef ROCKETLIB_MATHOPS_H +#define ROCKETLIB_MATHOPS_H + +#include + +#include "common/common.h" + +/** + * @brief Clamps a 32 bit unsigned integer between a lower and upper bound. + * + * @param x The value to clamp + * @param low The lower bound, inclusive + * @param high The upper bound, inclusive + * @return The clamped value + */ +static inline uint32_t value_clamp_uint32(uint32_t x, uint32_t low, uint32_t high) { + if (x < low) { + return low; + } else if (x > high) { + return high; + } else { + return x; + } +} + +/** + * @brief Clamps a 32 bit signed integer between a lower and upper bound. + * + * @param x The value to clamp + * @param low The lower bound, inclusive + * @param high The upper bound, inclusive + * @return The clamped value + */ +static inline int32_t value_clamp_int32(int32_t x, int32_t low, int32_t high) { + if (x < low) { + return low; + } else if (x > high) { + return high; + } else { + return x; + } +} + +/** + * @brief Clamps a 32 bit float between a lower and upper bound. + * + * @param x The value to clamp + * @param low The lower bound, inclusive + * @param high The upper bound, inclusive + * @return The clamped value + */ +static inline float32_t value_clamp_float32(float32_t x, float32_t low, float32_t high) { + if (x < low) { + return low; + } else if (x > high) { + return high; + } else { + return x; + } +} + +#endif diff --git a/dspic33e/dspic33e_can.c b/dspic33e/dspic33e_can.c deleted file mode 100644 index 802e6fe9..00000000 --- a/dspic33e/dspic33e_can.c +++ /dev/null @@ -1,271 +0,0 @@ -#include - -#include "dspic33e_can.h" - -static void (*can_rcv_cb)(const can_msg_t *message); - -// make a RAM buffer big enough to hold 32 CAN message -#define CAN_BUF_NUM 32 -static unsigned int can_msg_buf[CAN_BUF_NUM][8] __attribute__((aligned(CAN_BUF_NUM * 16))); - -// private functions -static void init_filters_and_masks(); -static void init_dma_channels(); -static void init_can_interrupts(); -static void buffer_to_msg(const unsigned int *buffer, can_msg_t *received); - -// Initialization function for CAN. The driver currently only runs in -// callback mode, and that callback will be triggered in an interrupt -// context. Please make the callback as short (in time) as possible. -void dspic33e_can_init(const can_timing_t *timing, - void (*receive_callback)(const can_msg_t *message), bool run_in_loopback) { - // store pointer to the receive callback - can_rcv_cb = receive_callback; - - // set can module to config mode - C1CTRL1bits.REQOP = 0x4; - // wait until that mode takes effect - while (C1CTRL1bits.OPMODE != 0x4) {} - - // allow writing of control bits - C1CTRL1bits.WIN = 1; - - // set baud rate and other timing details - C1CFG1bits.SJW = timing->sjw; - C1CFG1bits.BRP = timing->brp; - C1CFG2bits.SEG2PHTS = timing->btlmode; - C1CFG2bits.SAM = timing->sam; - C1CFG2bits.SEG1PH = timing->seg1ph; - C1CFG2bits.PRSEG = timing->prseg; - C1CFG2bits.SEG2PH = timing->seg2ph; - - // set up DMA and FIFO: don't use the FIFO, use 32 buffers for DMA - C1FCTRLbits.DMABS = 0b110; - C1FCTRLbits.FSA = 0b11111; - - // set up filters and masks - init_filters_and_masks(); - - // disable writes to C1CTRL - C1CTRL1bits.WIN = 0; - - // set the first transmit buffer as transmit, all others as - // receive buffers (we don't need plural transmits (I think)) - C1TR01CONbits.TXEN0 = 0x1; - C1TR01CONbits.TXEN1 = 0x0; - C1TR23CONbits.TXEN2 = 0x0; - C1TR23CONbits.TXEN3 = 0x0; - C1TR45CONbits.TXEN4 = 0x0; - C1TR45CONbits.TXEN5 = 0x0; - C1TR67CONbits.TXEN6 = 0x0; - C1TR67CONbits.TXEN7 = 0x0; - - // initialize the DMA module: This driver requires DMA0 and DMA1, - // so don't use those elsewhere - init_dma_channels(); - - // if you wanna run this driver in loopback mode, we can - // accomodate that - if (run_in_loopback) { - // set loopback mode - C1CTRL1bits.REQOP = 2; - // wait until change is applied - while (C1CTRL1bits.OPMODE != 0x2) {} - } else { - C1CTRL1bits.REQOP = 0; - while (C1CTRL1bits.OPMODE != 0) {} - } - - // initialize the interrupts - init_can_interrupts(); -} - -// Priority must be a 2 bit number defining how high the priority of -// this message is vs the other ones queued to be sent. 0 is lowest -// priority, 3 is highest -void dspic33e_can_send(const can_msg_t *message) { - // put the SID in buffer 0 of the DMA buffers - can_msg_buf[0][0] = ((message->sid >> 18) << 2) | 0x3; - can_msg_buf[0][1] = (message->sid >> 6) & 0xfff; - // set the length and rest of EID - can_msg_buf[0][2] = ((message->sid & 0x3f) << 10) | message->data_len; - - // copy data over. Note that this is a 16 bit part, so we - // interlace the bytes of data. To see the structure that the CAN - // module wants a CAN message to be in, look at datasheet section - // 21.5 - can_msg_buf[0][3] = ((message->data[1] << 8) | message->data[0]); - can_msg_buf[0][4] = ((message->data[3] << 8) | message->data[2]); - can_msg_buf[0][5] = ((message->data[5] << 8) | message->data[4]); - can_msg_buf[0][6] = ((message->data[7] << 8) | message->data[6]); - - // the message priority is always 3 (the highest) - C1TR01CONbits.TX0PRI = 3; - - C1TR01CONbits.TXREQ0 = 1; -} - -// Returns true if a message is ready to be sent -bool dspic33e_can_send_rdy(void) { - return C1TR01CONbits.TXREQ0 == 0; -} - -// Interrupt that runs whenever IFS2::C1IF is set -void __attribute__((__interrupt__, no_auto_psv)) _C1Interrupt(void) { - // a transmit successfully finished, we abort the message so it - // isn't set repeatedly - if (C1INTFbits.TBIF) { - C1INTFbits.TBIF = 0; - C1TR01CONbits.TXABT0 = 1; - return; - } - if (C1INTFbits.RBIF) { - // check all receive buffers, trigger the callback with every - // filled buffer - can_msg_t cb_message; - if (C1RXFUL1bits.RXFUL1) { - buffer_to_msg(can_msg_buf[1], &cb_message); - C1RXFUL1bits.RXFUL1 = 0; - can_rcv_cb(&cb_message); - } - if (C1RXFUL1bits.RXFUL2) { - buffer_to_msg(can_msg_buf[2], &cb_message); - C1RXFUL1bits.RXFUL2 = 0; - can_rcv_cb(&cb_message); - } - if (C1RXFUL1bits.RXFUL3) { - buffer_to_msg(can_msg_buf[3], &cb_message); - C1RXFUL1bits.RXFUL3 = 0; - can_rcv_cb(&cb_message); - } - if (C1RXFUL1bits.RXFUL4) { - buffer_to_msg(can_msg_buf[4], &cb_message); - C1RXFUL1bits.RXFUL4 = 0; - can_rcv_cb(&cb_message); - } - C1INTFbits.RBIF = 0; - return; - } - if (C1INTFbits.RBOVIF) { - // we weren't fast enough to catch all the messages - // in the future, raise a warning now - C1INTFbits.RBOVIF = 0; - return; - } - if (C1INTFbits.ERRIF) { - C1INTFbits.ERRIF = 0; - } - - // there are no other CAN interrupts that we want to deal - // with, so lower the main interrupt flag - IFS2bits.C1IF = 0; - return; -} - -// Converts the DsPIC's internal CAN message buffer format into our -// message struct format -static void buffer_to_msg(const unsigned int *buffer, can_msg_t *received) { - received->sid = ((buffer[0] >> 2) & 0x7ff) << 18; - received->sid |= (buffer[1] & 0xfff) << 6; - received->sid |= (buffer[2] >> 10) & 0x3f; - received->data_len = (buffer[2] & 0x0f); - // fallthroughs are intentional - switch (received->data_len) { - case 8: - received->data[7] = ((buffer[6] >> 8) & 0xff); - case 7: - received->data[6] = (buffer[6] & 0xff); - case 6: - received->data[5] = ((buffer[5] >> 8) & 0xff); - case 5: - received->data[4] = (buffer[5] & 0xff); - case 4: - received->data[3] = ((buffer[4] >> 8) & 0xff); - case 3: - received->data[2] = (buffer[4] & 0xff); - case 2: - received->data[1] = ((buffer[3] >> 8) & 0xff); - case 1: - received->data[0] = (buffer[3] & 0xff); - case 0: - default: - return; - } -} - -static void init_dma_channels() { - // use DMA channel 0 as the CAN Tx channel - DMA0CONbits.CHEN = 0; // disable for setup - DMA0CONbits.SIZE = 0; // set to word size, not byte size - DMA0CONbits.DIR = 1; // read from RAM, write to CAN - DMA0CONbits.AMODE = 0b10; // peripheral indirect addressing mode - DMA0CONbits.MODE = 0; // continuous mode, disable ping-pong mode - DMA0REQbits.IRQSEL = 0b01000110; // ECAN1-TX Data request - DMA0CNT = 7; // 8 words in a CAN message - DMA0PAD = (volatile uint16_t)&C1TXD; - DMA0STAL = (uint16_t)&can_msg_buf; - DMA0STAH = (uint16_t)&can_msg_buf; - DMA0CONbits.CHEN = 1; // we are go for DMA - - // enable DMA channel 1 as the CAN Rx channel - DMA1CONbits.CHEN = 0; - DMA1CONbits.SIZE = 0x0; - DMA1CONbits.DIR = 0x0; - DMA1CONbits.AMODE = 0x2; - DMA1CONbits.MODE = 0x0; - DMA1REQ = 34; - DMA1CNT = 7; - DMA1PAD = (volatile unsigned int)&C1RXD; - DMA1STAL = (unsigned int)&can_msg_buf; - DMA1STAH = (unsigned int)&can_msg_buf; - DMA1CONbits.CHEN = 0x1; -} - -static void init_can_interrupts() { - __builtin_enable_interrupts(); - - // put down a bunch of flags - C1RXFUL1 = 0; - C1RXFUL2 = 0; - C1RXOVF1 = 0; - C1RXOVF2 = 0; - C1INTFbits.RBIF = 0; - C1INTFbits.ERRIF = 0; - C1INTFbits.TBIF = 0; - - // enable interrupts on transmit and receive. Disable error interrupts - C1INTEbits.TBIE = 1; - C1INTEbits.RBIE = 1; - C1INTEbits.ERRIE = 0; - - // enable CAN1 interrupts - IEC2bits.C1IE = 1; -} - -// macro for the reduction of typing, only used in -// init_filters_and_masks. Sets filter n to point at mask 0, with -// filter SID 0, and cause the CAN buffer pointer for that filter to -// be n+1 -#define INIT_FILTER(n) \ - do { \ - /*point all filters at mask 0*/ \ - C1FMSKSEL1bits.F##n##MSK = 0; \ - /*set the filter SID to 0*/ \ - C1RXF##n##SID = 0; \ - /*point filter number at buffer (number+1)*/ \ - C1BUFPNT1bits.F##n##BP = ((n) + 1); \ - } while (0) - -// This driver does not currently support hardware filtering of -// incoming messages. All messages will be received, all will be -// handled the same through the callback. -static void init_filters_and_masks(void) { - // set mask 0 to accept all messages - C1RXM0SID = 0; - - // use 4 receive buffers in order to try to prevent overflows - INIT_FILTER(0); - INIT_FILTER(1); - INIT_FILTER(2); - INIT_FILTER(3); -} diff --git a/dspic33e/dspic33e_can.h b/dspic33e/dspic33e_can.h deleted file mode 100644 index 66982ada..00000000 --- a/dspic33e/dspic33e_can.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef CANLIB_DSPIC33E_CAN_H -#define CANLIB_DSPIC33E_CAN_H - -#include "can.h" - -#ifdef __cplusplus -#error "C++ is not supported" -#endif - -/** - * @brief Initialize DSPIC33E CAN Controller - * - * @param timing Timing parameters generated by `can_generate_timing_params` function - * @param receive_callback CAN message receive handler, the function would be called when a CAN - * message is received - * @param run_in_loopback Run CAN Controller in loopback mode when `true` - * - * @warning Note that this function DOES NOT setup the inputs and outputs from the CAN module to the - * output pins, application code must do that. In order to do that, CANRXPPS must be set to the - * proper pin value for the CANRX pin, and ___PPS must be set to 0x33 to mark it as outputting from - * the CAN module. In addition, TRIS and ANSEL registers for whatever pin is being used must be set - * to the right values. - */ -void dspic33e_can_init(const can_timing_t *timing, - void (*receive_callback)(const can_msg_t *message), bool run_in_loopback); - -/** - * @brief Send a CAN message - * - * @param message CAN message to be sent - */ -void dspic33e_can_send(const can_msg_t *message); - -/** - * @brief Check if CAN controller is ready to send a message - * - * @return return `true` if ready to send a message - */ -bool dspic33e_can_send_rdy(void); - -#endif diff --git a/flows/firmware-library.mk b/flows/firmware-library.mk new file mode 100644 index 00000000..52afcbe3 --- /dev/null +++ b/flows/firmware-library.mk @@ -0,0 +1,220 @@ +########################### +# User Configure Variable +########################### + +ENABLE_GCC_ANALYZER := 0 +ENABLE_SANITIZER := 0 + +########################### +# Source File Path Variables +########################### + +ROCKETTEST_SRCS := $(ROCKETLIB_SUBMODULE_PATH)/rockettest/rockettest.cpp +ROCKETTEST_HEADERS := $(ROCKETLIB_SUBMODULE_PATH)/rockettest/rockettest.hpp +ROCKETTEST_INCLUDE_PATH := $(ROCKETLIB_SUBMODULE_PATH)/rockettest + +CPP_SRCS := \ + $(ROCKETTEST_SRCS) \ + $(TEST_SRCS) + +########################### +# Common Build Variables +########################### + +INCLUDE_PATHS_C_CXX_FLAGS := $(foreach inc, $(INCLUDE_PATHS), $(addprefix -I, $(inc))) + +C_CXX_FLAGS += \ + $(INCLUDE_PATHS_C_CXX_FLAGS) \ + $(EXTRA_C_CXX_FLAGS) \ + -Wall \ + -Wextra \ + -pedantic \ + -MMD \ + -DUNIT_TEST \ + -O0 \ + -g + +ifeq ($(ENABLE_SANITIZER), 1) + C_CXX_FLAGS += -fsanitize=undefined + LDFLAGS += -fsanitize=address,undefined +endif + +ifeq ($(filter run-test,$(MAKECMDGOALS)),run-test) + BUILD_DIR := build/test +endif + +ifneq ($(filter run-test-cov gen-cov-html,$(MAKECMDGOALS)),) + BUILD_DIR := build/test-cov + + C_CXX_FLAGS += \ + -fprofile-arcs \ + -ftest-coverage \ + -fcondition-coverage \ + -fpath-coverage + + LDFLAGS += -Wl,-lgcov +endif + +CFLAGS := \ + $(C_CXX_FLAGS) \ + -std=c99 + +CXXFLAGS := \ + $(C_CXX_FLAGS) \ + -std=c++20 \ + -I$(ROCKETTEST_INCLUDE_PATH) + +ifeq ($(ENABLE_GCC_ANALYZER), 1) + CFLAGS += -fanalyzer +endif + +####################### +# XC8 Compile Variables +####################### + +XC8 := /opt/microchip/xc8/v3.10/bin/xc8-cc + +ifeq ($(filter xc8-build,$(MAKECMDGOALS)),xc8-build) + CC := $(XC8) + CFLAGS := -mcpu=18F26K83 -mdfp=$(XC8_DFP_PATH) -mwarn=-9 -std=c99 -Wpedantic $(INCLUDE_PATHS_C_CXX_FLAGS) $(EXTRA_C_CXX_FLAGS) + BUILD_DIR := build/xc8 +endif + +######################## +# XC16 Compile Variables +######################## + +XC16 := /opt/microchip/xc16/v2.10/bin/xc16-gcc + +ifeq ($(filter xc16-build,$(MAKECMDGOALS)),xc16-build) + CC := $(XC16) + CFLAGS := -mcpu=33EP256GP502 -std=c99 -Wall $(INCLUDE_PATHS_C_CXX_FLAGS) $(EXTRA_C_CXX_FLAGS) + BUILD_DIR := build/xc16 +endif + +#################### +# Linting Variables +#################### + +CLANG_TIDY := clang-tidy +INCLUDE_PATHS_CLANG_TIDY_FLAGS := $(foreach inc, $(INCLUDE_PATHS), $(addprefix --extra-arg-before="-I, $(addsuffix ", $(inc)))) +EXTRA_C_CXX_CLANG_TIDY_FLAGS := $(foreach flag, $(EXTRA_C_CXX_FLAGS), $(addprefix --extra-arg-before=", $(addsuffix ", $(flag)))) + +CLANG_TIDY_FLAGS := \ + --warnings-as-errors="*" \ + --checks="clang-*,misc-*" \ + --extra-arg-before="-std=c99" \ + --extra-arg-before="-pedantic" \ + $(INCLUDE_PATHS_CLANG_TIDY_FLAGS) \ + $(EXTRA_C_CXX_CLANG_TIDY_FLAGS) + +###################### +# Formatting Variables +###################### + +CLANG_FORMAT := clang-format + +########################################### +# Object File and Dependency File Variables +########################################### + +COMMON_C_OBJS = $(patsubst %.c,$(BUILD_DIR)/%.o,$(COMMON_C_SRCS)) +COMMON_C_DEPS = $(patsubst %.c,$(BUILD_DIR)/%.d,$(COMMON_C_SRCS)) + +PIC18_C_OBJS = $(patsubst %.c,$(BUILD_DIR)/%.o,$(PIC18_C_SRCS)) +PIC18_C_DEPS = $(patsubst %.c,$(BUILD_DIR)/%.d,$(PIC18_C_SRCS)) + +CPP_OBJS = $(patsubst %.cpp,$(BUILD_DIR)/%.o,$(CPP_SRCS)) +CPP_DEPS = $(patsubst %.cpp,$(BUILD_DIR)/%.d,$(CPP_SRCS)) + +###################### +# Object files compile +###################### + +$(BUILD_DIR)/%.o: %.c + @mkdir -p $(dir $@) + $(CC) $(CFLAGS) -c $< -o $@ + +$(BUILD_DIR)/%.o: %.cpp + @mkdir -p $(dir $@) + $(CXX) $(CXXFLAGS) -c $< -o $@ + +#################### +# Unit Test Build +#################### + +$(BUILD_DIR)/unit_test: $(COMMON_C_OBJS) $(CPP_OBJS) + @mkdir -p $(dir $@) + $(CXX) $^ $(LDFLAGS) -o $@ + +.PHONY: run-test +run-test: $(BUILD_DIR)/unit_test + ./$(BUILD_DIR)/unit_test + +############################### +# Unit Test with Coverage Build +############################### + +.PHONY: run-test-cov +run-test-cov: $(BUILD_DIR)/unit_test + ./$(BUILD_DIR)/unit_test + +$(BUILD_DIR)/coverage.info: run-test + lcov --capture --branch-coverage --mcdc-coverage --no-external --directory . -o $@ + +$(BUILD_DIR)/coverage-filtered.info: $(BUILD_DIR)/coverage.info + lcov --remove $^ "*/tests/*" "*/rockettest/*" -o $@ --branch-coverage --mcdc-coverage + +$(BUILD_DIR)/html: $(BUILD_DIR)/coverage-filtered.info + genhtml $^ --flat --branch-coverage --mcdc-coverage --output-directory $@ + +.PHONY: gen-cov-html +gen-cov-html: $(BUILD_DIR)/html + true + +#################### +# XC8 Build +#################### + +.PHONY: xc8-build +xc8-build: $(COMMON_C_OBJS) $(PIC18_C_OBJS) + true + +#################### +# XC16 Build +#################### + +.PHONY: xc16-build +xc16-build: $(COMMON_C_OBJS) + true + +#################### +# Linting +#################### + +.PHONY: lint +lint: + $(CLANG_TIDY) $(CLANG_TIDY_FLAGS) $(COMMON_C_SRCS) $(COMMON_C_HEADERS) + +#################### +# Formatting +#################### + +.PHONY: format +format: + $(CLANG_FORMAT) -i $(COMMON_C_SRCS) $(COMMON_C_HEADERS) $(PIC18_C_SRCS) $(PIC18_C_HEADERS) $(STM32H7_C_SRCS) $(STM32H7_C_HEADERS) $(TEST_SRCS) $(ROCKETTEST_SRCS) $(ROCKETTEST_HEADERS) + +.PHONY: format-check +format-check: + $(CLANG_FORMAT) --dry-run -Werror --style=file:$(ROCKETLIB_SUBMODULE_PATH)/.clang-format $(COMMON_C_SRCS) $(COMMON_C_HEADERS) $(PIC18_C_SRCS) $(PIC18_C_HEADERS) $(STM32H7_C_SRCS) $(STM32H7_C_HEADERS) $(TEST_SRCS) $(ROCKETTEST_SRCS) $(ROCKETTEST_HEADERS) + +-include $(COMMON_C_DEPS) +-include $(CPP_DEPS) + +#################### +# Clean +#################### + +.PHONY: clean +clean: + rm -rf build diff --git a/logging/mbr.c b/logging/mbr.c new file mode 100644 index 00000000..d570bf9d --- /dev/null +++ b/logging/mbr.c @@ -0,0 +1,42 @@ +#include + +#include "common/common.h" +#include "logging/mbr.h" + +#define BOOT_SIGNATURE_OFFSET 0x1fe +#define MBR_PT_OFFSET 0x1be +#define MBR_PT_SIZE 16 +#define MBR_TYPE_OFF 4 +#define MBR_LBA_OFF 8 + +w_status_t mbr_parse(uint8_t *first_sector, uint8_t partition_type, uint32_t *sector_lba) { + if (!first_sector || !sector_lba) { + return W_INVALID_PARAM; + } + + // Boot signature starts at 0x01FE in the first sector + uint8_t *boot_signature = first_sector + BOOT_SIGNATURE_OFFSET; + // Boot signature check + if (boot_signature[0] != 0x55 || boot_signature[1] != 0xaa) { + return W_FAILURE; + } + + // Set sector_lba to 0 as default + *sector_lba = 0; + + // First partition entry starts at 0x01BE in the first sector + uint8_t *entry = first_sector + MBR_PT_OFFSET; + + // Each partition entry has a size of 16 bytes + for (int i = 0; i < 4; i++, entry += MBR_PT_SIZE) { + // Partition type locates at 0x04 in each partition + uint8_t type = entry[MBR_TYPE_OFF]; + if (type == partition_type) { + *sector_lba = (uint32_t)entry[MBR_LBA_OFF] | ((uint32_t)entry[MBR_LBA_OFF + 1] << 8) | + ((uint32_t)entry[MBR_LBA_OFF + 2] << 16) | + ((uint32_t)entry[MBR_LBA_OFF + 3] << 24); + return W_SUCCESS; + } + } + return W_FAILURE; +} diff --git a/logging/mbr.h b/logging/mbr.h new file mode 100644 index 00000000..9cca1047 --- /dev/null +++ b/logging/mbr.h @@ -0,0 +1,31 @@ +#ifndef ROCKETLIB_MBR_H +#define ROCKETLIB_MBR_H + +#include +#include + +#include "common/common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief To find the starting sector of a given type of partition + * + * This function finds the lba for the partition that matches the partition type + * provided in the first sector. Put lba as 0 if the corresponding partition + * cannot be found. + * + * @param first_sector 512 byte buffer contains content of first sector + * @param partition_type The partition type trying to find + * @param sector_lba Pointer to the start offset of the sector + * @return w_status_t Returns W_SUCCESS on success, W_FAILURE if partition cannot be found + */ +w_status_t mbr_parse(uint8_t *first_sector, uint8_t partition_type, uint32_t *sector_lba); + +#ifdef __cplusplus +} +#endif + +#endif /* ROCKETLIB_MBR_H */ diff --git a/mcp2515/mcp2515.c b/mcp2515/mcp2515.c index 47b31602..6bb9ca67 100644 --- a/mcp2515/mcp2515.c +++ b/mcp2515/mcp2515.c @@ -1,6 +1,6 @@ #include -#include "can.h" +#include "can_message/can.h" #include "mcp2515.h" #include "mcp2515_regs.h" diff --git a/mcp2515/mcp2515.h b/mcp2515/mcp2515.h index 2d8730a3..f933903d 100644 --- a/mcp2515/mcp2515.h +++ b/mcp2515/mcp2515.h @@ -1,10 +1,10 @@ -#ifndef CANLIB_MCP2515_H -#define CANLIB_MCP2515_H +#ifndef ROCKETLIB_MCP2515_H +#define ROCKETLIB_MCP2515_H #include #include -#include "can.h" +#include "can_message/can.h" #ifdef __cplusplus #error "C++ is not supported" diff --git a/mcp2515/mcp2515_regs.h b/mcp2515/mcp2515_regs.h index c3faa196..924c8f92 100644 --- a/mcp2515/mcp2515_regs.h +++ b/mcp2515/mcp2515_regs.h @@ -1,5 +1,5 @@ -#ifndef CANLIB_MCP2515_REGS_H -#define CANLIB_MCP2515_REGS_H +#ifndef ROCKETLIB_MCP2515_REGS_H +#define ROCKETLIB_MCP2515_REGS_H // MCP2515 register addresses #define RXF0SIDH 0x00 diff --git a/pic18f26k83/pic18f26k83_can.c b/pic18f26k83/pic18f26k83_can.c index 0d1f3aca..50b86c11 100644 --- a/pic18f26k83/pic18f26k83_can.c +++ b/pic18f26k83/pic18f26k83_can.c @@ -1,7 +1,8 @@ #include #include -#include "pic18f26k83_can.h" +#include "pic18f26k83/pic18f26k83_can.h" +#include "pic18f26k83/pic18f26k83_can_timing_util.h" static void (*can_rcv_cb)(const can_msg_t *message); diff --git a/pic18f26k83/pic18f26k83_can.h b/pic18f26k83/pic18f26k83_can.h index 69bfbc79..ed6f9e74 100644 --- a/pic18f26k83/pic18f26k83_can.h +++ b/pic18f26k83/pic18f26k83_can.h @@ -1,9 +1,11 @@ -#ifndef CANLIB_PIC18F26K83_CAN_H -#define CANLIB_PIC18F26K83_CAN_H +#ifndef ROCKETLIB_PIC18F26K83_CAN_H +#define ROCKETLIB_PIC18F26K83_CAN_H #include -#include "can.h" +#include "can_message/can.h" + +#include "pic18f26k83/pic18f26k83_can_timing_util.h" #ifdef __cplusplus #error "C++ is not supported" diff --git a/util/timing_util.c b/pic18f26k83/pic18f26k83_can_timing_util.c similarity index 81% rename from util/timing_util.c rename to pic18f26k83/pic18f26k83_can_timing_util.c index 9ae97a2d..f7ebf183 100644 --- a/util/timing_util.c +++ b/pic18f26k83/pic18f26k83_can_timing_util.c @@ -1,15 +1,14 @@ #include -#include "common.h" +#include "common/common.h" -#include "can.h" -#include "timing_util.h" +#include "pic18f26k83/pic18f26k83_can_timing_util.h" -#if (CANLIB_BIT_TIME_US != 4) +#if (CAN_BIT_TIME_US != 4) #warning "the bit time that can.h is expecting is not what timing_util is expecting" #endif -w_status_t can_generate_timing_params(uint32_t system_freq, can_timing_t *timing) { +w_status_t pic18f26k83_can_generate_timing_params(uint32_t system_freq, can_timing_t *timing) { w_assert(timing); // this function is designed to create a bit time of 4 microseconds diff --git a/pic18f26k83/pic18f26k83_can_timing_util.h b/pic18f26k83/pic18f26k83_can_timing_util.h new file mode 100644 index 00000000..e1475fdf --- /dev/null +++ b/pic18f26k83/pic18f26k83_can_timing_util.h @@ -0,0 +1,48 @@ +#ifndef ROCKETLIB_TIMING_UTIL_H +#define ROCKETLIB_TIMING_UTIL_H + +#include +#include + +#include "common/common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define CAN_BIT_TIME_US 4 + +// Timing parameters +typedef struct { + // BaudRate Prescaler + uint8_t brp; + // Synchronization Jump Width + uint8_t sjw; + + // sample once or three times + uint8_t sam; + // phase segment 1 bits + uint8_t seg1ph; + // phase segment 2 bits + uint8_t seg2ph; + // propagation time segment bits + uint8_t prseg; + + // Phase segment 2 time select bit. If true, then use seg2ph, + // otherwise take minimum viable phase length + bool btlmode; +} can_timing_t; + +/** + * @brief Generate CAN bus timing setting for PIC microcontrollers + * @param system_freq PIC Fosc frequency in Hz + * @param timing buffer to write timing parameters to + * @return `W_SUCCESS` if system_freq is valid and parameters have been written to `timing` + */ +w_status_t pic18f26k83_can_generate_timing_params(uint32_t system_freq, can_timing_t *timing); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/pic18f26k83/pic18f26k83_timer.c b/pic18f26k83/pic18f26k83_timer.c new file mode 100644 index 00000000..257271bc --- /dev/null +++ b/pic18f26k83/pic18f26k83_timer.c @@ -0,0 +1,58 @@ +#include + +#include "pic18f26k83/pic18f26k83_timer.h" + +/* + * Magic numbers: timer is driven on a 500kHz clock and is 8 bits. + * Timer rolls over every 256 counts, or every 512 microseconds. + * MILLIS_INCREMENT = 0, since there are 0 ms added each rollover + * MILLIS_REMAINDER = 512, added each time the timer is triggered + * MILLIS_INCREMENT_CAP = 1000, number of microseconds before we tick over a millisecond + * + * Only the ratio between MILLIS_REMAINDER and MILLIS_INCREMENT_CAP matters, so + * we can optimize things a little bit by dividing both by 8 so that the largest + * value of internal_count can fit within 8 bits. + * + */ +#define MILLIS_INCREMENT 0 +#define MILLIS_REMAINDER 64 +#define MILLIS_INCREMENT_CAP 125 + +volatile static uint32_t millis_counter = 0; + +uint32_t millis(void) { + INTCON0bits.GIE = 0; + uint32_t res = millis_counter; + INTCON0bits.GIE = 1; + return res; +} + +void timer0_init(void) { + PIE3bits.TMR0IE = 1; // enable timer 0 interrupt + + T0CON0bits.EN = 0; // disable timer module + T0CON0bits.MD16 = 0; // set up timer as 8 bits + + T0CON0bits.OUTPS = 0; // set the output prescaler to 1:1 + T0CON1bits.CKPS = 0; // set the prescaler rate to 1:1 + + T0CON1bits.CS = 0x5; // drive timer from 500 kHz + T0CON1bits.ASYNC = 0; // timer zero is synchronized to the system clock + + // enable the module + T0CON0bits.EN = 1; +} + +/* + * Based on Bresenham's algorithm and described here: http://romanblack.com/one_sec.htm + */ +void timer0_handle_interrupt() { + static uint8_t internal_count = 0; + + millis_counter += MILLIS_INCREMENT; + internal_count += MILLIS_REMAINDER; + if (internal_count > MILLIS_INCREMENT_CAP) { + internal_count -= MILLIS_INCREMENT_CAP; + millis_counter++; + } +} diff --git a/pic18f26k83/pic18f26k83_timer.h b/pic18f26k83/pic18f26k83_timer.h new file mode 100644 index 00000000..c252ef8a --- /dev/null +++ b/pic18f26k83/pic18f26k83_timer.h @@ -0,0 +1,48 @@ +/** + * @file + * @brief PIC18 Timer driver provides millis function + * + * This module provides basic timer functionalities, such as initializing Timer0, handling + * interrupts, and tracking milliseconds since the timer was started. + */ + +#ifndef ROCKETLIB_TIMER_H +#define ROCKETLIB_TIMER_H + +#include + +#ifdef __cplusplus +#error "C++ is not supported" +#endif + +/** + * @brief Initializes Timer0 for time tracking. This function must be called before using any other + * timer-related functionality. + * + * - Enables Timer0 interrupt. + * - Sets the timer to 8-bit mode. + * - Driven by a 500 kHz clock. + * - Ensures the timer is synchronized to the system clock. + */ +void timer0_init(void); + +/** + * @brief Function should be called from main ISR when Timer0 interrupt is triggered + * + * This function should be called from the main Interrupt Service Routine (ISR) whenever the Timer0 + * interrupt is triggered. It updates the internal time counter. + * + * @warning This function does **not** clear the interrupt flag (`PIR3bits.TMR0IF`), This is the + * responsibility of the top level ISR. + */ +void timer0_handle_interrupt(void); + +/** + * @brief Returns the number of milliseconds since `timer0_init()` was called + * + * Returns the number of milliseconds since `timer0_init()` was called. This is a safe function that + * disables interrupts momentarily to return a consistent value. + */ +uint32_t millis(void); + +#endif /* ROCKETLIB_TIMER_H */ diff --git a/rocketlib b/rocketlib deleted file mode 160000 index 21a7cb53..00000000 --- a/rocketlib +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 21a7cb53cd32961cf31a4ee1fcc118ded65d46bd diff --git a/rocketlib.h b/rocketlib.h new file mode 100644 index 00000000..eb04cc45 --- /dev/null +++ b/rocketlib.h @@ -0,0 +1,31 @@ +#ifndef ROCKETLIB_ROCKETLIB_H +#define ROCKETLIB_ROCKETLIB_H + +// Common headers +#include "common/common.h" +#include "common/crc8.h" +#include "common/electrical.h" +#include "common/low_pass_filter.h" +#include "common/mathops.h" + +// CAN message headers +#include "can_message/can.h" +#include "can_message/msg_actuator.h" +#include "can_message/msg_canards.h" +#include "can_message/msg_common.h" +#include "can_message/msg_general.h" +#include "can_message/msg_gps.h" +#include "can_message/msg_recovery.h" +#include "can_message/msg_sensor.h" +#include "can_message/msg_stream.h" +#include "can_message/msg_telemetry.h" + +// CAN buffer headers +#include "can_buffer/can_rcv_buffer.h" +#include "can_buffer/can_tx_buffer.h" +#include "can_buffer/safe_ring_buffer.h" + +// Logging headers +#include "logging/mbr.h" + +#endif diff --git a/rockettest/rockettest.cpp b/rockettest/rockettest.cpp new file mode 100644 index 00000000..d1c751ae --- /dev/null +++ b/rockettest/rockettest.cpp @@ -0,0 +1,101 @@ +#include +#include +#include +#include +#include +#include + +#include "rockettest.hpp" + +int main(int argc, char *argv[]) { + int seed = std::time({}); + + int opt; + while ((opt = getopt(argc, argv, "s:")) != -1) { + switch (opt) { + case 's': + seed = atoi(optarg); + break; + + default: + std::cout << "Usage: unit_test [-s random_seed] [test_names..]" << std::endl; + std::cout << "If random_seed is not provided then current time is used as seed" + << std::endl; + std::cout << "If test_names are not provided then all tests will run" << std::endl; + return EXIT_FAILURE; + } + } + + std::cout << "Random seed " << seed << std::endl; + std::srand(seed); + + bool all_passed = true; + + if (optind < argc) { + // Run selected tests + for (; optind < argc; optind++) { + if (rockettest_test::tests.contains(argv[optind])) { + if (!(*rockettest_test::tests[argv[optind]])()) { + all_passed = false; + } + } else { + all_passed = false; + std::cout << CONSOLE_COLOUR_RED << "NOT FOUND " << CONSOLE_COLOUR_RESET + << argv[optind] << std::endl; + } + } + + } else { + // Run all test + for (auto &rt : rockettest_test::tests) { + if (!(*rt.second)()) { + all_passed = false; + } + } + } + + if (all_passed) { + return EXIT_SUCCESS; + } + + return EXIT_FAILURE; +} + +std::map rockettest_test::tests; + +rockettest_test::rockettest_test(const char *test_name) : name(test_name) { + tests[test_name] = this; +} + +bool rockettest_test::operator()() { + std::cout << "Running " << name << std::endl; + bool test_result = run_test(); + if (test_result == true) { + std::cout << CONSOLE_COLOUR_GREEN << "PASSED " << CONSOLE_COLOUR_RESET << name << std::endl; + } else { + std::cout << CONSOLE_COLOUR_RED << "FAILED " << CONSOLE_COLOUR_RESET << name << std::endl; + } + return test_result; +} + +static std::jmp_buf assert_test_env; + +void rocketlib_assert_pass_cpp(const char *file, int line, const char *statement) {} + +void rocketlib_assert_fail_cpp(const char *file, int line, const char *statement) { + std::longjmp(assert_test_env, 1); +} + +extern "C" void rocketlib_assert_pass_c(const char *file, int line, const char *statement) {} + +extern "C" void rocketlib_assert_fail_c(const char *file, int line, const char *statement) { + std::longjmp(assert_test_env, 1); +} + +bool rockettest_check_assert_sjlj(std::function funccall) { + if (setjmp(assert_test_env) == 0) { + funccall(); + return false; + } + return true; +} diff --git a/rockettest/rockettest.hpp b/rockettest/rockettest.hpp new file mode 100644 index 00000000..34315f7a --- /dev/null +++ b/rockettest/rockettest.hpp @@ -0,0 +1,86 @@ +#ifndef ROCKETTEST_H +#define ROCKETTEST_H + +#include +#include +#include +#include +#include +#include + +/** + * Base functionality + */ + +#define CONSOLE_COLOUR_RESET "\033[0m" +#define CONSOLE_COLOUR_RED "\033[1;31m" +#define CONSOLE_COLOUR_GREEN "\033[1;32m" + +#define rockettest_check_expr_true(expr) \ + if (!(expr)) { \ + std::cout << CONSOLE_COLOUR_RED << "E: " << CONSOLE_COLOUR_RESET \ + << "Expression should be true " << __FILE__ << ':' << __LINE__ << ' ' << #expr \ + << std::endl; \ + test_passed = false; \ + } + +#define rockettest_check_assert_triggered(funccall) \ + if (!rockettest_check_assert_sjlj((funccall))) { \ + std::cout << CONSOLE_COLOUR_RED << "E: " << CONSOLE_COLOUR_RESET \ + << "Rocketlib w_assert did not trigger on " << __FILE__ << ':' << __LINE__ \ + << ' ' << #funccall << std::endl; \ + test_passed = false; \ + } + +class rockettest_test { + const char *name; + +public: + static std::map tests; + + rockettest_test() = delete; + rockettest_test(const char *test_name); + + virtual bool run_test() = 0; + + bool operator()(); +}; + +bool rockettest_check_assert_sjlj(std::function funccall); + +/** + * Random Number Generation + */ + +template T rockettest_rand_field() { + return static_cast(std::rand() & mask); +} + +template +T rockettest_rand_field_with_exclude(T exclude) { + T rn = static_cast(std::rand() & mask); + if (rn == exclude) { + rn = (rn + 1) & mask; + } + return rn; +} + +// Generate a random number in [min,max) +template T rockettest_rand_range(T min, T max) { + return static_cast((std::rand() % (static_cast(max) - static_cast(min))) + + static_cast(min)); +} + +template T rockettest_rand_range_with_exclude(T min, T max, T exclude) { + T rn = static_cast((std::rand() % (static_cast(max) - static_cast(min))) + + static_cast(min)); + if (rn == exclude) { + rn += 1; + if (rn == max) { + rn = min; + } + } + return rn; +} + +#endif diff --git a/stm32h7/stm32h7_can.h b/stm32h7/stm32h7_can.h index 7219e6dd..2ccc6638 100644 --- a/stm32h7/stm32h7_can.h +++ b/stm32h7/stm32h7_can.h @@ -1,11 +1,11 @@ -#ifndef CANLIB_STM32H7_CAN_H -#define CANLIB_STM32H7_CAN_H +#ifndef ROCKETLIB_STM32H7_CAN_H +#define ROCKETLIB_STM32H7_CAN_H #include #include "stm32h7xx_hal.h" -#include "can.h" +#include "can_message/can.h" #ifdef __cplusplus extern "C" { diff --git a/tests/test_crc8.cpp b/tests/test_crc8.cpp new file mode 100644 index 00000000..c174adc8 --- /dev/null +++ b/tests/test_crc8.cpp @@ -0,0 +1,27 @@ +#include + +#include "rocketlib.h" + +#include "rockettest.hpp" + +class crc8_test : rockettest_test { +public: + crc8_test() : rockettest_test("crc8_test") {} + + bool run_test() override { + bool test_passed = true; + + rockettest_check_assert_triggered( + [] { crc8_checksum(nullptr, 0, 0); }); // nullptr reject with zero input size + + rockettest_check_assert_triggered( + [] { crc8_checksum(nullptr, 5, 0); }); // nullptr reject with non-zero input size + + const uint8_t input[5] = {'A', 'B', 'C', 'D', 'E'}; + rockettest_check_expr_true(crc8_checksum(input, sizeof(input), 0) == 0xf5); + + return test_passed; + } +}; + +crc8_test crc8_test_inst; diff --git a/tests/test_low_pass_filter.cpp b/tests/test_low_pass_filter.cpp new file mode 100644 index 00000000..7689ad70 --- /dev/null +++ b/tests/test_low_pass_filter.cpp @@ -0,0 +1,89 @@ +#include +#include +#include +#include +#include + +#include "rocketlib.h" + +#include "rockettest.hpp" + +#define EPSILON 0.0001 + +class low_pass_filter_init_test : rockettest_test { +public: + low_pass_filter_init_test() : rockettest_test("low_pass_filter_init_test") {} + + bool run_test() override { + bool test_passed = true; + + double alpha; + // Test with valid input + rockettest_check_expr_true(low_pass_filter_init(&alpha, 1.0) == W_SUCCESS); + // Test with invalid input (NULL pointer) + rockettest_check_expr_true(low_pass_filter_init(NULL, 1.0) == W_INVALID_PARAM); + // Test with invalid input (negative response time) + rockettest_check_expr_true(low_pass_filter_init(&alpha, -1.0) == W_INVALID_PARAM); + // Test with invalid input (zero response time) + rockettest_check_expr_true(low_pass_filter_init(&alpha, 0.0) == W_INVALID_PARAM); + + return test_passed; + } +}; + +low_pass_filter_init_test low_pass_filter_init_test_inst; + +class low_pass_filter_update_test : rockettest_test { +public: + low_pass_filter_update_test() : rockettest_test("low_pass_filter_update_test") {} + + bool run_test() override { + bool test_passed = true; + + double alpha = 0.1; // Smoothing factor (0 < alpha < 1) + double low_pass_value = 0.0; // Initial low-pass filtered value + uint16_t new_input_value = 100; // Fixed input value to test behavior + int iterations = 50; // Number of times to apply the filter + w_status_t result; + + printf("Testing Low-Pass Filter with Fixed Input\n"); + printf("-----------------------------------------\n"); + printf("Alpha (smoothing factor): %.2f\n", alpha); + printf("Input Value: %u\n\n", new_input_value); + + for (int i = 1; i <= iterations; ++i) { + // Store the previous low_pass_value before updating + double previous_low_pass = low_pass_value; + // Calculate the expected value based on the low-pass filter formula + double expected = (alpha * new_input_value) + ((1.0 - alpha) * previous_low_pass); + // Update the low-pass filter with the fixed input value + result = update_low_pass(alpha, new_input_value, &low_pass_value); + rockettest_check_expr_true(result == W_SUCCESS); + // Print the current iteration and values + printf("Iteration %2d: Low-Pass Value = %.4f | Expected ~ %.4f\n", i, low_pass_value, + expected); + // Verify the correctness of the low-pass update + rockettest_check_expr_true(fabs(low_pass_value - expected) < EPSILON); + } + + printf("\nAll iterations completed successfully.\n\n"); + + // Test with alpha out of range + result = update_low_pass(-0.1, new_input_value, &low_pass_value); + rockettest_check_expr_true(result == W_INVALID_PARAM); + printf("Invalid alpha (negative) test passed.\n"); + + result = update_low_pass(1.1, new_input_value, &low_pass_value); + rockettest_check_expr_true(result == W_INVALID_PARAM); + printf("Invalid alpha (greater than 1) test passed.\n"); + + // Test with NULL low_pass_value + result = update_low_pass(alpha, new_input_value, NULL); + rockettest_check_expr_true(result == W_INVALID_PARAM); + printf("Null low_pass_value test passed.\n"); + + return test_passed; + } +}; + +low_pass_filter_update_test low_pass_filter_update_test_inst; diff --git a/tests/test_mathops.cpp b/tests/test_mathops.cpp new file mode 100644 index 00000000..4382c4b5 --- /dev/null +++ b/tests/test_mathops.cpp @@ -0,0 +1,129 @@ +#include +#include +#include +#include + +#include "rocketlib.h" + +#include "rockettest.hpp" + +class value_clamp_uint32_test : rockettest_test { +public: + value_clamp_uint32_test() : rockettest_test("value_clamp_uint32_test") {} + + bool run_test() override { + bool test_passed = true; + + printf("value_clamp_uint32: in-range and boundary behavior\n"); + rockettest_check_expr_true(value_clamp_uint32(50U, 10U, 100U) == 50U); + rockettest_check_expr_true(value_clamp_uint32(10U, 10U, 100U) == 10U); + rockettest_check_expr_true(value_clamp_uint32(100U, 10U, 100U) == 100U); + + printf("value_clamp_uint32: out-of-range behavior\n"); + rockettest_check_expr_true(value_clamp_uint32(9U, 10U, 100U) == 10U); + rockettest_check_expr_true(value_clamp_uint32(101U, 10U, 100U) == 100U); + + printf("value_clamp_uint32: extreme values\n"); + rockettest_check_expr_true(value_clamp_uint32(0U, 1U, 2U) == 1U); + rockettest_check_expr_true( + value_clamp_uint32(std::numeric_limits::max(), 1U, 2U) == 2U); + rockettest_check_expr_true( + value_clamp_uint32(0U, 0U, std::numeric_limits::max()) == 0U); + rockettest_check_expr_true(value_clamp_uint32(std::numeric_limits::max(), 0U, + std::numeric_limits::max()) == + std::numeric_limits::max()); + + printf("value_clamp_uint32: interval with zero width\n"); + rockettest_check_expr_true(value_clamp_uint32(0U, 42U, 42U) == 42U); + rockettest_check_expr_true(value_clamp_uint32(42U, 42U, 42U) == 42U); + rockettest_check_expr_true(value_clamp_uint32(100U, 42U, 42U) == 42U); + + return test_passed; + } +}; + +value_clamp_uint32_test value_clamp_uint32_test_inst; + +class value_clamp_int32_test : rockettest_test { +public: + value_clamp_int32_test() : rockettest_test("value_clamp_int32_test") {} + + bool run_test() override { + bool test_passed = true; + + printf("value_clamp_int32: in-range and boundary behavior\n"); + rockettest_check_expr_true(value_clamp_int32(0, -10, 10) == 0); + rockettest_check_expr_true(value_clamp_int32(-10, -10, 10) == -10); + rockettest_check_expr_true(value_clamp_int32(10, -10, 10) == 10); + + printf("value_clamp_int32: out-of-range behavior\n"); + rockettest_check_expr_true(value_clamp_int32(-11, -10, 10) == -10); + rockettest_check_expr_true(value_clamp_int32(11, -10, 10) == 10); + + printf("value_clamp_int32: extreme values\n"); + rockettest_check_expr_true(value_clamp_int32(std::numeric_limits::min(), -5, 5) == + -5); + rockettest_check_expr_true(value_clamp_int32(std::numeric_limits::max(), -5, 5) == + 5); + rockettest_check_expr_true(value_clamp_int32(std::numeric_limits::min(), + std::numeric_limits::min(), + std::numeric_limits::max()) == + std::numeric_limits::min()); + rockettest_check_expr_true(value_clamp_int32(std::numeric_limits::max(), + std::numeric_limits::min(), + std::numeric_limits::max()) == + std::numeric_limits::max()); + + printf("value_clamp_int32: interval with zero width\n"); + rockettest_check_expr_true(value_clamp_int32(-100, 7, 7) == 7); + rockettest_check_expr_true(value_clamp_int32(7, 7, 7) == 7); + rockettest_check_expr_true(value_clamp_int32(100, 7, 7) == 7); + + return test_passed; + } +}; + +value_clamp_int32_test value_clamp_int32_test_inst; + +class value_clamp_float32_test : rockettest_test { +public: + value_clamp_float32_test() : rockettest_test("value_clamp_float32_test") {} + + bool run_test() override { + bool test_passed = true; + + printf("value_clamp_float32: in-range and boundary behavior\n"); + rockettest_check_expr_true(value_clamp_float32(0.5F, -1.0F, 1.0F) == 0.5F); + rockettest_check_expr_true(value_clamp_float32(-1.0F, -1.0F, 1.0F) == -1.0F); + rockettest_check_expr_true(value_clamp_float32(1.0F, -1.0F, 1.0F) == 1.0F); + + printf("value_clamp_float32: out-of-range behavior\n"); + rockettest_check_expr_true(value_clamp_float32(-2.0F, -1.0F, 1.0F) == -1.0F); + rockettest_check_expr_true(value_clamp_float32(2.0F, -1.0F, 1.0F) == 1.0F); + + printf("value_clamp_float32: finite extreme values\n"); + rockettest_check_expr_true( + value_clamp_float32(std::numeric_limits::lowest(), -1.0F, 1.0F) == -1.0F); + rockettest_check_expr_true( + value_clamp_float32(std::numeric_limits::max(), -1.0F, 1.0F) == 1.0F); + + printf("value_clamp_float32: infinity behavior\n"); + rockettest_check_expr_true(value_clamp_float32(-std::numeric_limits::infinity(), + -10.0F, 10.0F) == -10.0F); + rockettest_check_expr_true(value_clamp_float32(std::numeric_limits::infinity(), + -10.0F, 10.0F) == 10.0F); + + printf("value_clamp_float32: NaN behavior\n"); + const float32_t nan_value = std::numeric_limits::quiet_NaN(); + rockettest_check_expr_true(std::isnan(value_clamp_float32(nan_value, -1.0F, 1.0F))); + + printf("value_clamp_float32: interval with zero width\n"); + rockettest_check_expr_true(value_clamp_float32(-5.0F, 3.5F, 3.5F) == 3.5F); + rockettest_check_expr_true(value_clamp_float32(3.5F, 3.5F, 3.5F) == 3.5F); + rockettest_check_expr_true(value_clamp_float32(9.0F, 3.5F, 3.5F) == 3.5F); + + return test_passed; + } +}; + +value_clamp_float32_test value_clamp_float32_test_inst; diff --git a/tests/test_mbr.cpp b/tests/test_mbr.cpp new file mode 100644 index 00000000..53ff2cdd --- /dev/null +++ b/tests/test_mbr.cpp @@ -0,0 +1,67 @@ +#include + +#include "rocketlib.h" + +#include "rockettest.hpp" + +class mbr_test : rockettest_test { +public: + mbr_test() : rockettest_test("mbr_test") {} + + bool run_test() override { + bool test_passed = true; + + // Example partition type + uint8_t partition_type = 0x07; + + // Declare sector lba + uint32_t sector_lba; + + // Example first sector + uint8_t first_sector[512] = {0}; + uint8_t *entry = first_sector + 0x1be; + uint8_t *boot_signature = first_sector + 0x1fe; + + entry[0] = 0x80; // boot flag + entry[4] = partition_type; // partition type + // Imaginary LBA: 0x001100000 + entry[8] = 0x00; // LBA low byte (LBA is little-endian) + entry[9] = 0x00; + entry[10] = 0x11; + entry[11] = 0x00; // LBA high byte + + // Test with invalid both bytes of boot signature + rockettest_check_expr_true(mbr_parse(first_sector, partition_type, §or_lba) == + W_FAILURE); + + boot_signature[1] = 0xaa; + // Test with invalid first byte of boot signature + rockettest_check_expr_true(mbr_parse(first_sector, partition_type, §or_lba) == + W_FAILURE); + + boot_signature[0] = 0x55; + boot_signature[1] = 0x00; + // Test with invalid second byte of boot signature + rockettest_check_expr_true(mbr_parse(first_sector, partition_type, §or_lba) == + W_FAILURE); + + boot_signature[0] = 0x55; + boot_signature[1] = 0xaa; + + // Test with valid input + rockettest_check_expr_true(mbr_parse(first_sector, partition_type, §or_lba) == + W_SUCCESS); + // Test with invalid input (NULL first sector) + rockettest_check_expr_true(mbr_parse(nullptr, partition_type, §or_lba) == + W_INVALID_PARAM); + // Test with invalid input (NULL LBA) + rockettest_check_expr_true(mbr_parse(first_sector, partition_type, nullptr) == + W_INVALID_PARAM); + // Test with a partition type that cannot be found + rockettest_check_expr_true(mbr_parse(first_sector, 0x06, §or_lba) == W_FAILURE); + + return test_passed; + } +}; + +mbr_test mbr_test_inst; diff --git a/tests/test_msg_actuator.cpp b/tests/test_msg_actuator.cpp index 268668e3..6cc79157 100644 --- a/tests/test_msg_actuator.cpp +++ b/tests/test_msg_actuator.cpp @@ -1,13 +1,13 @@ #include #include -#include "common.h" +#include "common/common.h" #include "rockettest.hpp" -#include "can.h" -#include "message/msg_actuator.h" -#include "message/msg_common.h" -#include "message_types.h" +#include "can_message/can.h" +#include "can_message/msg_actuator.h" +#include "can_message/msg_common.h" +#include "can_message/packet_format.h" class actuator_cmd_message_test : rockettest_test { public: diff --git a/tests/test_msg_canards.cpp b/tests/test_msg_canards.cpp index e3850578..acb24013 100644 --- a/tests/test_msg_canards.cpp +++ b/tests/test_msg_canards.cpp @@ -2,10 +2,10 @@ #include "rockettest.hpp" -#include "can.h" -#include "message/msg_canards.h" -#include "message/msg_common.h" -#include "message_types.h" +#include "can_message/can.h" +#include "can_message/msg_canards.h" +#include "can_message/msg_common.h" +#include "can_message/packet_format.h" class canard_firmware_error_message_test : rockettest_test { public: diff --git a/tests/test_msg_common.cpp b/tests/test_msg_common.cpp index 1bca4204..12c1dafb 100644 --- a/tests/test_msg_common.cpp +++ b/tests/test_msg_common.cpp @@ -1,10 +1,10 @@ #include -#include "message_types.h" +#include "can_message/packet_format.h" #include "rockettest.hpp" -#include "can.h" -#include "message/msg_common.h" +#include "can_message/can.h" +#include "can_message/msg_common.h" class timestamp_test : rockettest_test { public: diff --git a/tests/test_msg_general.cpp b/tests/test_msg_general.cpp index 723b34b1..b20873e3 100644 --- a/tests/test_msg_general.cpp +++ b/tests/test_msg_general.cpp @@ -1,12 +1,12 @@ #include -#include "common.h" +#include "common/common.h" #include "rockettest.hpp" -#include "can.h" -#include "message/msg_common.h" -#include "message/msg_general.h" -#include "message_types.h" +#include "can_message/can.h" +#include "can_message/msg_common.h" +#include "can_message/msg_general.h" +#include "can_message/packet_format.h" class general_status_message_test : rockettest_test { public: diff --git a/tests/test_msg_gps.cpp b/tests/test_msg_gps.cpp index 389231df..79ce782e 100644 --- a/tests/test_msg_gps.cpp +++ b/tests/test_msg_gps.cpp @@ -2,10 +2,10 @@ #include "rockettest.hpp" -#include "can.h" -#include "message/msg_common.h" -#include "message/msg_gps.h" -#include "message_types.h" +#include "can_message/can.h" +#include "can_message/msg_common.h" +#include "can_message/msg_gps.h" +#include "can_message/packet_format.h" class gps_timestamp_message_test : rockettest_test { public: diff --git a/tests/test_msg_recovery.cpp b/tests/test_msg_recovery.cpp index 40865e76..ed58d0e9 100644 --- a/tests/test_msg_recovery.cpp +++ b/tests/test_msg_recovery.cpp @@ -2,10 +2,10 @@ #include "rockettest.hpp" -#include "can.h" -#include "message/msg_common.h" -#include "message/msg_recovery.h" -#include "message_types.h" +#include "can_message/can.h" +#include "can_message/msg_common.h" +#include "can_message/msg_recovery.h" +#include "can_message/packet_format.h" class recovery_cmd_message_test : rockettest_test { public: diff --git a/tests/test_msg_sensor.cpp b/tests/test_msg_sensor.cpp index a97f301e..754d523b 100644 --- a/tests/test_msg_sensor.cpp +++ b/tests/test_msg_sensor.cpp @@ -2,10 +2,10 @@ #include "rockettest.hpp" -#include "can.h" -#include "message/msg_common.h" -#include "message/msg_sensor.h" -#include "message_types.h" +#include "can_message/can.h" +#include "can_message/msg_common.h" +#include "can_message/msg_sensor.h" +#include "can_message/packet_format.h" namespace { diff --git a/tests/test_msg_stream.cpp b/tests/test_msg_stream.cpp index 99d0228e..a61e0f92 100644 --- a/tests/test_msg_stream.cpp +++ b/tests/test_msg_stream.cpp @@ -1,13 +1,13 @@ #include #include -#include "common.h" +#include "common/common.h" #include "rockettest.hpp" -#include "can.h" -#include "message/msg_common.h" -#include "message/msg_stream.h" -#include "message_types.h" +#include "can_message/can.h" +#include "can_message/msg_common.h" +#include "can_message/msg_stream.h" +#include "can_message/packet_format.h" namespace { diff --git a/tests/test_msg_telemetry.cpp b/tests/test_msg_telemetry.cpp index 1844ee53..33a2cf91 100644 --- a/tests/test_msg_telemetry.cpp +++ b/tests/test_msg_telemetry.cpp @@ -2,10 +2,10 @@ #include "rockettest.hpp" -#include "can.h" -#include "message/msg_common.h" -#include "message/msg_telemetry.h" -#include "message_types.h" +#include "can_message/can.h" +#include "can_message/msg_common.h" +#include "can_message/msg_telemetry.h" +#include "can_message/packet_format.h" class telemetry_info_message_test : rockettest_test { public: diff --git a/tests/test_nullptr_reject.cpp b/tests/test_nullptr_reject.cpp index 9965f690..eb23c5b5 100644 --- a/tests/test_nullptr_reject.cpp +++ b/tests/test_nullptr_reject.cpp @@ -1,22 +1,9 @@ #include -#include "can.h" -#include "message_types.h" #include "rockettest.hpp" -#include "message/msg_actuator.h" -#include "message/msg_canards.h" -#include "message/msg_common.h" -#include "message/msg_general.h" -#include "message/msg_gps.h" -#include "message/msg_recovery.h" -#include "message/msg_sensor.h" -#include "message/msg_stream.h" -#include "message/msg_telemetry.h" -#include "util/can_rcv_buffer.h" -#include "util/can_tx_buffer.h" -#include "util/safe_ring_buffer.h" -#include "util/timing_util.h" +#include "pic18f26k83/pic18f26k83_can_timing_util.h" +#include "rocketlib.h" namespace { @@ -580,7 +567,8 @@ class msg_ptr_nullptr_reject_test : rockettest_test { // ----------------------- // timing_util tests // ----------------------- - rockettest_check_assert_triggered([] { can_generate_timing_params(48000000, nullptr); }); + rockettest_check_assert_triggered( + [] { pic18f26k83_can_generate_timing_params(48000000, nullptr); }); return test_passed; } }; diff --git a/tests/test_rockettest.cpp b/tests/test_rockettest.cpp new file mode 100644 index 00000000..e79c1fd4 --- /dev/null +++ b/tests/test_rockettest.cpp @@ -0,0 +1,152 @@ +#include +#include + +#include "rockettest.hpp" + +class test_rand_field : rockettest_test { +public: + test_rand_field() : rockettest_test("test_rand_field") {} + + bool run_test() override { + bool test_passed = true; + + int dist[256]{}; + + for (int i = 0; i < 10000; i++) { + uint8_t randval = rockettest_rand_field(); + dist[randval]++; + } + + int min_occurrence = 10000; + int max_occurrence = 0; + + for (int k : dist) { + rockettest_check_expr_true(k >= 1); + if (k > max_occurrence) { + max_occurrence = k; + } + if (k < min_occurrence) { + min_occurrence = k; + } + } + + std::cout << "Max Occurrence = " << max_occurrence << std::endl; + std::cout << "Min Occurrence = " << min_occurrence << std::endl; + + return test_passed; + } +}; + +test_rand_field test_rand_field_inst; + +class test_rand_range : rockettest_test { +public: + test_rand_range() : rockettest_test("test_rand_range") {} + + bool run_test() override { + bool test_passed = true; + + int dist[100]{}; + + for (int i = 0; i < 10000; i++) { + uint8_t randval = rockettest_rand_range(100, 200); + rockettest_check_expr_true(randval >= 100); + rockettest_check_expr_true(randval < 200); + dist[randval - 100]++; + } + + int min_occurrence = 10000; + int max_occurrence = 0; + + for (int k : dist) { + rockettest_check_expr_true(k >= 1); + if (k > max_occurrence) { + max_occurrence = k; + } + if (k < min_occurrence) { + min_occurrence = k; + } + } + + std::cout << "Max Occurrence = " << max_occurrence << std::endl; + std::cout << "Min Occurrence = " << min_occurrence << std::endl; + + return test_passed; + } +}; + +test_rand_range test_rand_range_inst; + +class test_rand_field_with_exclude : rockettest_test { +public: + test_rand_field_with_exclude() : rockettest_test("test_rand_field_with_exclude") {} + + bool run_test() override { + bool test_passed = true; + + int dist[256]{}; + + for (int i = 0; i < 10000; i++) { + uint8_t randval = rockettest_rand_field_with_exclude(120); + rockettest_check_expr_true(randval != 120); + dist[randval]++; + } + + int min_occurrence = 10000; + int max_occurrence = 0; + + for (int k : dist) { + if (k > max_occurrence) { + max_occurrence = k; + } + if ((k < min_occurrence) && (k > 0)) { + min_occurrence = k; + } + } + + std::cout << "Max Occurrence = " << max_occurrence << std::endl; + std::cout << "Min Non-Zero Occurrence = " << min_occurrence << std::endl; + + return test_passed; + } +}; + +test_rand_field_with_exclude test_rand_field_with_exclude_inst; + +class test_rand_range_with_exclude : rockettest_test { +public: + test_rand_range_with_exclude() : rockettest_test("test_rand_range_with_exclude") {} + + bool run_test() override { + bool test_passed = true; + + int dist[100]{}; + + for (int i = 0; i < 10000; i++) { + uint8_t randval = rockettest_rand_range_with_exclude(100, 200, 150); + rockettest_check_expr_true(randval >= 100); + rockettest_check_expr_true(randval < 200); + rockettest_check_expr_true(randval != 150); + dist[randval - 100]++; + } + + int min_occurrence = 10000; + int max_occurrence = 0; + + for (int k : dist) { + if (k > max_occurrence) { + max_occurrence = k; + } + if ((k < min_occurrence) && (k > 0)) { + min_occurrence = k; + } + } + + std::cout << "Max Occurrence = " << max_occurrence << std::endl; + std::cout << "Min Non-Zero Occurrence = " << min_occurrence << std::endl; + + return test_passed; + } +}; + +test_rand_range_with_exclude test_rand_range_with_exclude_inst; diff --git a/tests/test_timing_util.cpp b/tests/test_timing_util.cpp index 63e1efd9..f29db475 100644 --- a/tests/test_timing_util.cpp +++ b/tests/test_timing_util.cpp @@ -1,8 +1,8 @@ -#include "common.h" +#include "common/common.h" #include "rockettest.hpp" -#include "can.h" -#include "util/timing_util.h" +#include "can_message/can.h" +#include "pic18f26k83/pic18f26k83_can_timing_util.h" class timing_util_test : public rockettest_test { public: @@ -14,7 +14,8 @@ class timing_util_test : public rockettest_test { can_timing_t timing; // 48 MHz, for example PIC18 with 12 MHz crystal and 4xPLL enabled - rockettest_check_expr_true(can_generate_timing_params(48000000, &timing) == W_SUCCESS); + rockettest_check_expr_true(pic18f26k83_can_generate_timing_params(48000000, &timing) == + W_SUCCESS); rockettest_check_expr_true(timing.brp == 7); rockettest_check_expr_true(timing.sjw == 3); rockettest_check_expr_true(timing.btlmode == 1); @@ -24,7 +25,8 @@ class timing_util_test : public rockettest_test { rockettest_check_expr_true(timing.seg2ph == 4); // 12 MHz, for example PIC18 with 12 MHz crystal and 4xPLL disabled - rockettest_check_expr_true(can_generate_timing_params(12000000, &timing) == W_SUCCESS); + rockettest_check_expr_true(pic18f26k83_can_generate_timing_params(12000000, &timing) == + W_SUCCESS); rockettest_check_expr_true(timing.brp == 1); rockettest_check_expr_true(timing.sjw == 3); rockettest_check_expr_true(timing.btlmode == 1); @@ -34,7 +36,8 @@ class timing_util_test : public rockettest_test { rockettest_check_expr_true(timing.seg2ph == 4); // 6 MHz, currently not used on physical hardware - rockettest_check_expr_true(can_generate_timing_params(6000000, &timing) == W_SUCCESS); + rockettest_check_expr_true(pic18f26k83_can_generate_timing_params(6000000, &timing) == + W_SUCCESS); rockettest_check_expr_true(timing.brp == 0); rockettest_check_expr_true(timing.sjw == 3); rockettest_check_expr_true(timing.btlmode == 1); @@ -44,7 +47,8 @@ class timing_util_test : public rockettest_test { rockettest_check_expr_true(timing.seg2ph == 4); // Invalid system frequency - rockettest_check_expr_true(can_generate_timing_params(0, &timing) == W_INVALID_PARAM); + rockettest_check_expr_true(pic18f26k83_can_generate_timing_params(0, &timing) == + W_INVALID_PARAM); return test_passed; } diff --git a/tests/test_tx_rcv_buffer.cpp b/tests/test_tx_rcv_buffer.cpp index 0ea87cc2..4c95a37b 100644 --- a/tests/test_tx_rcv_buffer.cpp +++ b/tests/test_tx_rcv_buffer.cpp @@ -3,12 +3,9 @@ #include #include -#include "can.h" -#include "common.h" #include "rockettest.hpp" -#include "util/can_rcv_buffer.h" -#include "util/can_tx_buffer.h" +#include "rocketlib.h" // Tx Buffer Test diff --git a/util/timing_util.h b/util/timing_util.h deleted file mode 100644 index a3aa86db..00000000 --- a/util/timing_util.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef CANLIB_TIMING_UTIL_H -#define CANLIB_TIMING_UTIL_H - -#include -#include - -#include "common.h" - -#include "can.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief Generate CAN bus timing setting for PIC microcontrollers - * @param system_freq PIC Fosc frequency in Hz - * @param timing buffer to write timing parameters to - * @return `W_SUCCESS` if system_freq is valid and parameters have been written to `timing` - */ -w_status_t can_generate_timing_params(uint32_t system_freq, can_timing_t *timing); - -#ifdef __cplusplus -} -#endif - -#endif