Skip to content

v4.0.0

Latest
Compare
Choose a tag to compare
@thomas-bc thomas-bc released this 07 Aug 21:47
abb09e4

Summary

We are excited to release F Prime v4.0! This release is a major upgrade from the latest v3 series and includes numerous improvements, new features, and breaking changes that enhance the framework's capabilities and maintainability.

This release comes with the following amazing features:

  1. Conditional Sequencing (Svc/FpySequencer)
  2. Improved CMake architecture
  3. Support for CCSDS communication protocols (TC, TM and Space Packet)
  4. FPP v3, including:
    • Telemetry packet definitions (allowing removal of all XML dependencies)
    • Type aliases
    • Externally managed parameters
    • Exporting of type configurations to GDS dictionary
    • Improved code generation for strings
    • Code generation supporting improvements in F Prime
    • FPP Interfaces
  5. Better subtopology support and addition of core subtopologies in Svc.Subtopologies
  6. Better GDS plugin support (GdsStandardApp, DataHandlerPlugin)
  7. Fixed-Width Numerical Types
  8. Formalized configuration modules
  9. Design pattern documentation
  10. Code Consistency Improvements

Release v4.0 is a significant upgrade and comes with multiple breaking changes that were introduced in order to consolidate design patterns and bring them in line with our coding standards. Below is a list of breaking changes as well as instructions on how to upgrade a v3.6.x project to v4.

Table of Contents

These changes are broken down into the following categories:

Tip

Some changes are required and others are optional. The 🚨 symbol designates a breaking change that may apply to projects wishing to migrate to v4.

Typing Changes

F Prime is removing the NATIVE_INT_TYPE, PlatformIntType, etc. Additionally, F Prime has begun ensuring that configurable types (e.g. FwIndexType) are configured to fixed-width values. The requirements (signed, minimum sizes) can be found in the numerical types document.

Users needing non-default values for configurable types should set them as type aliases in FPP using the new configuration system.

Warning

All Typing changes are required for upgrade to v4.0.0.

Port Indicies and NATIVE_INT_TYPE and NATIVE_UINT_TYPE FwIndexType Removal

NATIVE_INT_TYPE must be replaced with FwIndexType in port indices. Other uses of NATIVE_INT_TYPE and NATIVE_UINT_TYPE must also be replaced as these violate the fixed-width type standard.

For other configurable type options, see numerical types design.

Before:

const NATIVE_INT_TYPE portNum

After:

const FwIndexType portNum

Rate Group Contexts

Rate group context has been changed to the fixed-width type U32 to meet compliance with fixed-with types usage.

Before

NATIVE_INT_TYPE rateGroup1Context[Svc::ActiveRateGroup::CONNECTION_COUNT_MAX] = {};
NATIVE_INT_TYPE rateGroup2Context[Svc::ActiveRateGroup::CONNECTION_COUNT_MAX] = {};
NATIVE_INT_TYPE rateGroup3Context[Svc::ActiveRateGroup::CONNECTION_COUNT_MAX] = {};

After:

U32 rateGroup1Context[Svc::ActiveRateGroup::CONNECTION_COUNT_MAX] = {};
U32 rateGroup2Context[Svc::ActiveRateGroup::CONNECTION_COUNT_MAX] = {};
U32 rateGroup3Context[Svc::ActiveRateGroup::CONNECTION_COUNT_MAX] = {};

Unit Test Constant Changes

Use of NATIVE_INT_TYPE has been removed in unit tests.

Before:

    static const NATIVE_INT_TYPE MAX_HISTORY_SIZE = 10;

    // Instance ID supplied to the component instance under test
    static const NATIVE_INT_TYPE TEST_INSTANCE_ID = 0;

    // Queue depth supplied to the component instance under test
    static const NATIVE_INT_TYPE TEST_INSTANCE_QUEUE_DEPTH = 10;

After:

    // Maximum size of histories storing events, telemetry, and port outputs
    static const U32 MAX_HISTORY_SIZE = 10;

    // Instance ID supplied to the component instance under test
    static const FwEnumStoreType TEST_INSTANCE_ID = 0;

    // Queue depth supplied to the component instance under test
    static const FwSizeType TEST_INSTANCE_QUEUE_DEPTH = 10;

Fw::Buffer size type

The Fw::Buffer size type has been changed to FwSizeType (previously U32). This allows for configuration and better integration with other types. Switch to using FwSizeType when reading / setting Fw::Buffer sizes.

Struct Member Access

C++ getters and setters for fields of FPP structs are now autocoded in the form get_<field>() and set_<field>() (used to be get<field>() and set<field>()). C++ code using those getters and setters must be updated.

Component Changes

Component implementations in v4.0.0 have also changed.

Removal of PRIVATE, PROTECTED, and STATIC

Macro definitions PRIVATE, PROTECTED, and STATIC were as close to redefining a language feature as one could get without technically redefining a language feature. Users must switch to friend classes for white-box unit tests.

Warning

This change is required.

Unittest base classes are automatically and a tester classes are automatically added as friends to the component's base class to enable unit testing. Users may implement test-access to the base class through their tester class (e.g. SignalGenTester).

Users needing access to the component implementation class should add a friendship statement by hand.

Component Configuration Usage Changes

Configuration headers have been placed in the name-spacing folder config. This is to reduce the possibility of collision with project and external headers. Configuration paths must be updated. FpConfig.hpp/FpConfig.h has been updated to Fw/FPrimeBasicTypes.hpp/Fw/FPrimeBasicTypes.h and a dependency on Fw_Types is now explicitly required.

Warning

These changes are required and affects configuration headers and autocoded configuration headers.

Before:

#include "IpCfg.hpp"

After:

#include "config/IpCfg.hpp"

Before: C++

#include "FpConfig.hpp"

After: C++

#include "Fw/FPrimeBasicTypes.hpp

Before: CMakeLists.txt

set(MOD_DEPS
    SomeDependency
)

After: CMakeLists.txt

set(MOD_DEPS
    Fw_Types
    SomeDependency
)

Tip

Fw_Types is auto-detected for FPP-based modules. Add this dependency when not using FPP autocoding on your module.

Built-In Component Changes

  • FileUplink and FileDownlink now prepend the buffers (file packets) they send out with the appropriate FW_PACKET_FILE marker, as it is expected for all F´ data packets.

  • ComQueue has been updated to handle buffer management according the "Data Return" pattern mentioned above.

  • Svc.ActiveLogger has been renamed to Svc.EventManager

  • Drv.BlockDriver has been removed. Users can use a timer component to tick the RateGroupDriver instead, such as Svc.LinuxTimer. An example of such a changeset can be found here: fprime-tutorial-hello-world#46

  • The Drv.ByteStreamDriver interface has been slightly modified and the Drv.AsyncByteStreamDriver has been introduced. The Svc.ComStub component has been updated to work with either of those interfaces.

  • <interface>.fppi style interfaces have been replaced with format FPP interface/import. Examples can be found in Svc/Interfaces/ and Drv/Interfaces/

Deployment Changes

Deployments in v4.0.0 have substantially changed, and F´ now ships subtopologies to help construct deployments with minimal copy-and-paste from F´ into users topologies.

Tip

It may be easiest to reconstruct a deployment using fprime-util new --deployment as it will stamp-out a basic subtopology-powered deployment. Users would need to re-add their custom component instances and connections.

Pre-built subtopologies

F Prime now ships with the following subtopologies that users can use to reduce the size and complexity of their topology.

Tip

This change is not required but can help adopting required changes more easily.

Subtopology Description
CdhCore - Command dispatching and event management
- Event logging and telemetry collection
- Health monitoring system
- Fatal error handling
ComCcsds - CCSDS protocol implementation
- Frame processing and routing
ComFprime - F Prime protocol implementation
- Frame processing and routing
FileHandling - File upload and download services
- Parameter database management
- File system operations
DataProducts - Data product cataloging
- Storage and retrieval capabilities
- Product metadata management

Before:

instance cmdDisp: Svc.CommandDispatcher base id CdhCoreConfig.BASE_ID + 0x00000 \
[...]

topology MyTopology {
    #Active Components
    instance cmdDisp
[...]
}

connections FaultProtection {
    events.FatalAnnounce -> fatalHandler.FatalReceive
}
[...]

After:

import CdhCore.Subtopology

Communications component stack

The Uplink and Downlink components have been updated for better modularity and to allow for easy support of other communication protocols.

Tip

Rather than making all these changes by hand, users can instead import the Comms subtopologies.

Example Change Set

If using a standard Uplink/Downlink stack as generated by fprime-util new --deployment, the full change-set that users need to apply to their topology is shown in the LedBlinker change log

Click to Expand changes to a topology.fpp

Before (topology.fpp)

connections Uplink {
      comDriver.allocate -> bufferManager.bufferGetCallee
      comDriver.$recv -> comStub.drvDataIn
      comStub.comDataOut -> deframer.framedIn
      deframer.framedDeallocate -> bufferManager.bufferSendIn
      deframer.comOut -> cmdDisp.seqCmdBuff
      cmdDisp.seqCmdStatus -> deframer.cmdResponseIn
      deframer.bufferAllocate -> bufferManager.bufferGetCallee
      deframer.bufferOut -> fileUplink.bufferSendIn
      deframer.bufferDeallocate -> bufferManager.bufferSendIn
      fileUplink.bufferSendOut -> bufferManager.bufferSendIn
}
connections Downlink {
    eventLogger.PktSend -> comQueue.comQueueIn[0]
    tlmSend.PktSend -> comQueue.comQueueIn[1]
    fileDownlink.bufferSendOut -> comQueue.buffQueueIn[0]
    comQueue.comQueueSend -> framer.comIn
    comQueue.buffQueueSend -> framer.bufferIn
    framer.framedAllocate -> bufferManager.bufferGetCallee
    framer.framedOut -> comStub.comDataIn
    framer.bufferDeallocate -> fileDownlink.bufferReturn
    comDriver.deallocate -> bufferManager.bufferSendIn
    comDriver.ready -> comStub.drvConnected
    comStub.comStatus -> framer.comStatusIn
    framer.comStatusOut -> comQueue.comStatusIn
    comStub.drvDataOut -> comDriver.$send
}

After (topology.fpp)

enum Ports_ComPacketQueue {
    EVENTS,
    TELEMETRY
}

enum Ports_ComBufferQueue {
    FILE_DOWNLINK
}

connections Uplink {
      # ComDriver buffer allocations
      comDriver.allocate      -> bufferManager.bufferGetCallee
      comDriver.deallocate    -> bufferManager.bufferSendIn
      # ComDriver <-> ComStub
      comDriver.$recv             -> comStub.drvReceiveIn
      comStub.drvReceiveReturnOut -> comDriver.recvReturnIn
      # ComStub <-> FrameAccumulator
      comStub.dataOut                -> frameAccumulator.dataIn
      frameAccumulator.dataReturnOut -> comStub.dataReturnIn
      # FrameAccumulator buffer allocations
      frameAccumulator.bufferDeallocate -> bufferManager.bufferSendIn
      frameAccumulator.bufferAllocate   -> bufferManager.bufferGetCallee
      # FrameAccumulator <-> Deframer
      frameAccumulator.dataOut -> deframer.dataIn
      deframer.dataReturnOut   -> frameAccumulator.dataReturnIn
      # Deframer <-> Router
      deframer.dataOut           -> fprimeRouter.dataIn
      fprimeRouter.dataReturnOut -> deframer.dataReturnIn
      # Router buffer allocations
      fprimeRouter.bufferAllocate   -> bufferManager.bufferGetCallee
      fprimeRouter.bufferDeallocate -> bufferManager.bufferSendIn
      # Router <-> CmdDispatcher/FileUplink
      fprimeRouter.commandOut  -> cmdDisp.seqCmdBuff
      cmdDisp.seqCmdStatus     -> fprimeRouter.cmdResponseIn
      fprimeRouter.fileOut     -> fileUplink.bufferSendIn
      fileUplink.bufferSendOut -> fprimeRouter.fileBufferReturnIn
}

connections Downlink {
    # Inputs to ComQueue (events, telemetry, file)
    eventLogger.PktSend        -> comQueue.comPacketQueueIn[Ports_ComPacketQueue.EVENTS]
    tlmSend.PktSend            -> comQueue.comPacketQueueIn[Ports_ComPacketQueue.TELEMETRY]
    fileDownlink.bufferSendOut -> comQueue.bufferQueueIn[Ports_ComBufferQueue.FILE_DOWNLINK]
    comQueue.bufferReturnOut[Ports_ComBufferQueue.FILE_DOWNLINK] -> fileDownlink.bufferReturn
    # ComQueue <-> Framer
    comQueue.dataOut           -> fprimeFramer.dataIn
    fprimeFramer.dataReturnOut -> comQueue.dataReturnIn
    # Buffer Management for Framer
    fprimeFramer.bufferAllocate   -> commsBufferManager.bufferGetCallee
    fprimeFramer.bufferDeallocate -> commsBufferManager.bufferSendIn
    # Framer <-> ComStub
    fprimeFramer.dataOut  -> comStub.dataIn
    comStub.dataReturnOut -> fprimeFramer.dataReturnIn
    # ComStub <-> ComDriver
    comStub.drvSendOut      -> comDriver.$send
    comDriver.sendReturnOut -> comStub.drvSendReturnIn
    comDriver.ready         -> comStub.drvConnected
    # ComStatus
    comStub.comStatusOut       -> fprimeFramer.comStatusIn
    fprimeFramer.comStatusOut  -> comQueue.comStatusIn
}
Click to Expand changes to a instances.fpp

Diff (instances.fpp)

# Note: Make sure to adjust the base IDs to fit in your deployment
- instance framer: Svc.Framer base id 0x4100
+ instance framer: Svc.FprimeFramer base id 0x4100

- instance deframer: Svc.Deframer base id 0x4900
+ instance deframer: Svc.FprimeDeframer base id 0x4900

+ instance frameAccumulator: Svc.FrameAccumulator base id 0x4D00
+ instance fprimeRouter: Svc.FprimeRouter base id 0x4E00
Click to Expand changes to a Topology.cpp

The following removes the old framing/deframing protocols, and introduces the new FprimeFrameDetector.

Diff (Topology.cpp)

- #include <Svc/FramingProtocol/FprimeProtocol.hpp>
+ #include <Svc/FrameAccumulator/FrameDetector/FprimeFrameDetector.hpp>

- Svc::FprimeFraming framing;
- Svc::FprimeDeframing deframing;
+ Svc::FrameDetectors::FprimeFrameDetector frameDetector;
[...]
- framer.setup(framing);
- deframer.setup(deframing);
+ frameAccumulator.configure(frameDetector, 1, mallocator, 2048);

Uplink Changes

The old Svc.Deframer was performing 3 functionalities: (1) accumulating bytes in a circular buffer until it detects a full frame, (2) validating the frame and extracting the payload data, and (3) routing payload data to its destination.

In the new Uplink stack, these 3 functionalities have been split into 3 distinct components: (1) Svc.FrameAccumulator, (2) Svc.FprimeDeframer, and (3) Svc.FprimeRouter. To learn more about these components, please check out their SDDs on the website!

Each component implements an FPP Interface (in Svc/Interfaces/) so that they can be swapped with project-specific components, allowing for custom Deframing / Routing.

Memory Management

Memory management in the Uplink/Downlink stack has been updated so that a buffer coming out of a component on dataOut shall come back on dataReturnIn. This allows for components to allocate/deallocate memory as they see fit, and for the Topology Engineer not to have to track down memory management of each component to make sure they are wired correctly with the appropriate BufferManagers.

Downlink Changes

In the Downlink stack, the port connections have been modified to fit the new memory management pattern. The Framer now implements the FramerInterface.fppi to allow projects to implement custom Framing easily.

Build System Changes

These changes affect how the CMake system is used.

New CMake Module Structure

The old module registration structure in F Prime had one primary limitation: SOURCE_FILES and MOD_DEPS were variables and thus could bleed into other module registrations if not unset. This pollution of CMake's variable namespace, high chance for user error, and poor choice of the name "MOD_DEPS" led to a need to refactor how modules are done. To fit in with modern CMake practices, all module inputs are arguments to the registration calls with individual variables specified by directive arguments (e.g. SOURCES, DEPENDS).

Tip

register_fprime_module, register_fprime_deployment and register_fprime_ut still support MOD_DEPS, SOURCE_FILES, UT_MOD_DEPS, UT_SOURCE_FILES. Updating to the new structure is only required for register_fprime_configuration calls. However, new features will only be supported with the new structure and as such, users are encouraged to update when needed.

The new register_fprime_* calls are provided arguments lists separated by argument directives to specify sources (SOURCES), dependencies (DEPENDS) etc.

The first argument is an optional explicit module name followed by directives and their argument lists.

Before:

set(SOURCE_FILES
    "${CMAKE_CURRENT_LIST_DIR}/source1.cpp"
    "${CMAKE_CURRENT_LIST_DIR}/source2.cpp"
    "${CMAKE_CURRENT_LIST_DIR}/source1.fpp"
    "${CMAKE_CURRENT_LIST_DIR}/source2.fpp"
)
set(HEADER_FILES
    "${CMAKE_CURRENT_LIST_DIR}/header1.hpp"
    "${CMAKE_CURRENT_LIST_DIR}/header2.hpp"
)
set(MOD_DEPS Fw_Types)
register_fprime_module(MyModule)

After:

register_fprime_module(
    SOURCES
        "${CMAKE_CURRENT_LIST_DIR}/source1.cpp"
        "${CMAKE_CURRENT_LIST_DIR}/source2.cpp"
    HEADERS
        "${CMAKE_CURRENT_LIST_DIR}/header1.hpp"
        "${CMAKE_CURRENT_LIST_DIR}/header2.hpp"
    AUTOCODER_INPUTS
        "${CMAKE_CURRENT_LIST_DIR}/source1.fpp"
        "${CMAKE_CURRENT_LIST_DIR}/source2.fpp"
    DEPENDS
        Fw_Types
)

Tip

Notice that autocoder inputs are now specifically called out separately from compiled source files.

Warning

Do not specify an explicit module name when autocoding FPP.

Old variable usage can be translated to new directives using the following table:

Old Structure's Variable Directive Purpose
SOURCE_FILES SOURCES Source files supplied to cc, c++, or other compiler
SOURCE_FILES AUTOCODER_INPUTS Autocoder input files used to generate file
MOD_DEPS DEPENDS Module build target dependencies
HEADER_FILES HEADERS Header files supplied by the module
UT_SOURCE_FILES (built) SOURCES Unit test source files to supplied to compiler
UT_SOURCE_FILES (autocode inputs) AUTOCODER_INPUTS Unit test autocoder input files
UT_MOD_DEPS DEPENDS Unit test module dependencies
UT_HEADER_FILES HEADERS Unit test headers

Deployment Ordering

Since deployments in F Prime do recursive detection of items like unit tests, etc, deployments now check for the existence of F Prime modules that support them. This means F Prime deployments must be defined last in the CMake structure.

Warning

These changes are required.

Before:

add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Components/")

add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/MathDeployment/")

add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Types/")
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Ports/")

After:

add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Components/")
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Types/")
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Ports/")

add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/MathDeployment/")

Project Configuration Changes

One of the flaws of historical F Prime is that configuration was an all-or-nothing copy. It meant that projects, libraries, etc could not just override what was changed. This presented projects with a maintenance issue: owning unchanged code provided by F Prime while tracking their own minor changes.

With v4.0.0 projects choose to override specific files and the rest are inherited from underlying configuration modules.

Warning

These changes are required.

Additionally, each configuration is specified as a module. Use the CONFIGURATION_OVERRIDES directive to override existing config. User SOURCES, HEADERS, and AUTOCODER_INPUTS as usual to specify new configuration (i.e. new configuration for your library). Modules specifying only CONFIGURATION_OVERRIDES must also use the INTERFACE specifier.

To specify a new configuration module, ensure that some directory added to the project with add_fprime_subdirectory contains a CMakeLists.txt including a register_fprime_config call.

See changes in cmake module structure to understand the format of the register_fprime_config call.

CMakeLists.txt

    register_fprime_config(
      CONFIGURATION_OVERRIDES
        "${CMAKE_CURRENT_LIST_DIRECTORY}/FpConfig.fpp"
        "${CMAKE_CURRENT_LIST_DIRECTORY}/IpCfg.hpp"
    )

This example shows how to override just FpConifg.fpp and IpCfg.hpp from fprime.

Tip

Configurations are overridden by filename, so remember to keep your filenames consistent with the file you wish to override.

Tip

Default F Prime configuration lives at fprime/default/config projects will use these settings unless the filename is included in a projects configuration module via CONFIGURATION_OVERRIDES.

Warning

F Prime only has the notion of a single configuration per project (i.e. build). You still may not specify different configurations for different modules built by the same project.

Platform Changes

Platforms have had several major changes in v4.0.0:

  1. Platform types have been moved into FPP as type aliases
  2. Platform types are expected to use fixed-with types
  3. Platform setup should now be a configuration module via register_fprime_config

Warning

All changes in this section are required.

Platform Types

Platform types were previously defined as header typedefs, however; this meant these types (which flow into configurable types) are not available to FPP models. These types are now aliases within FPP.

Before (PlatformTypes.h)

typedef uint64_t PlatformSizeType;
...

After (PlatformTypes.fpp)

@ The unsigned type of larger sizes internal to the software,
@ e.g., memory buffer sizes, file sizes. Must be unsigned.
@ Supplied by platform, overridable by project.
type PlatformSizeType = U64
...

Warning

Since PlatfromPointerCast should not be used in the model, it is kept in PlatformTypes.h

The requirements on these types, and the complete list are found in the numerical types documentation.

Platform Definitions

Platform definitions are defined in cmake/platform/<name-of-platform>.cmake. This file must now define an fprime configuration module that performs the autocoding for the new PlatformTypes.fpp

Before

include_directories(SYSTEM "${CMAKE_CURRENT_LIST_DIR}/types")

After

register_fprime_config(
    AUTOCODER_INPUTS
        "${CMAKE_CURRENT_LIST_DIR}/PlatformTypes.fpp"
    HEADERS
        "${CMAKE_CURRENT_LIST_DIR}/PlatformTypes.h"
    INTERFACE # No buildable files generated
)

Other breaking changes 🚨

  • The OSAL method Os::FileSystem::getPathType() is now implementation-specific. OSAL implementations should implement this method if they wish to use it in their project.
  • XML is no longer supported as a modeling language or intermediary output. Users should switch to the FPP features.
  • CMake Implementations have been updated. Please refer to CMake Implementations Guide

Deprecations

  • Fw::Buffer::getSerializeRepr() has been deprecated in favor of two simpler-to-use methods for serialization and deserialization: Fw::Buffer::getSerializer() and Fw::Buffer::getDeserializer(). Example changesets are found #3431.
  • The [de]serialization function variants [de]serialize(U8* buff, Serializable::SizeType& length, bool noLength) are deprecated as they do not adhere to coding guidelines. Users should switch to the [de]serialize(U8* buff, FwSizeType& length, Serialization::t mode) variants instead.
  • The serialization methods [de]serialize() will soon be deprecated. Users are recommended to switch to the serializeTo() and deserializeFrom() methods.

Other Significant Changes

These changes should not affect user developed code.

Subtopologies

F Prime v4 introduces better support for subtopologies (specification, configuration, etc.), which allow for better modularity and organization of components within a topology. Subtopologies can be used to group related components together, making it easier to manage complex systems. Core subtopologies are also available in Svc.Subtopologies. New deployment generation has been switched to use these subtopologies.

FPP v3

F Prime v4 ships with many significant new features to FPP (from FPP v3 release notes):

  • Telemetry packet definitions
  • Type aliases
  • Externally managed parameters
  • Exporting of type configurations to GDS dictionary
  • Improved code generation for strings
  • Code generation supporting improvements in F Prime
  • Bug fixes

Miscellaneous

  • Use of Os::StubMutex will now only cause an error in the case that there should have been mutex contention. In cases where the lock is uncontested, no error will result.
  • The CMake system has been switched to use Ninja by default instead of Makefiles. This comes with no changes to the user experience as Ninja can be shipped with the core pip requirements.txt. Build time should be improved.
  • The code base has been formatted for better readability and consistency.
  • Many, many, many bug fixes, minor improvements, and documentation updates have been made. See the full list of changes below for more details.
  • fprime-gds now defaults to using the CCSDS protocols. Users who want to retain the F´ protocol should use fprime-gds --framing-selection fprime 🚨

What's Changed

Click to Expand Full list of changes

New Contributors

Full Changelog: v3.6.3...v4.0.0