cFS Architecture (avionics/fsw/cfs-architecture)
Use when the task is NASA core Flight Software (cFS) architecture: the
cFE/OSAL/PSP layering, the core flight executive (cFE) applications, the
classic app lifecycle pattern, or software bus publish/subscribe routing
by 16-bit message ID. The module simulates the software bus and event
service in pure Python: you register apps, subscribe to message IDs,
publish payloads, route queued messages in publish order, stamp telemetry
sequence counters, and log events by severity.
Domain quick reference
- cFS is the NASA open-source flight software framework: an application
platform (cFE), an OS abstraction layer (OSAL), and a board support
layer (PSP), with flight apps running on top. Layering bottom to top:
hardware, PSP, OSAL, cFE core, flight apps. cFE 6.x is the reference
baseline; the project is maintained on GitHub (nasa/cFS, Apache-2.0).
- PSP (Platform Support Package) is the only board-specific layer: CPU
reset, clock/timer, memory, EEPROM, and console support. One cFE binary
tree, one PSP per target processor.
- OSAL (OS Abstraction Layer) wraps the real-time OS (RTEMS, VxWorks,
POSIX) behind one API: tasks, semaphores, queues, timers, mutexes,
filesystem, network. Apps never call the RTOS directly.
- cFE (core Flight Executive) provides the reusable services:
- Executive Services (ES): app startup/stop, memory pools, housekeeping
catalog, reset control.
- Software Bus (SB): publish/subscribe message routing by message ID,
the backbone of all inter-app communication.
- Event Services (EVS): severity-filtered event log for operator and
FDIR visibility.
- Table Services (TBL): loadable configuration tables with validation.
- Time Services (TIME): time source management, time sync messages.
- File Services (FS): filesystem utilities, file headers, file/disk
housekeeping.
- Software bus routing: a message ID (16-bit in classic cFS) selects the
destination. Apps subscribe to message IDs; publishers call
CFE_SB_SendMsg; SB copies the message to every subscriber's pipe.
Command traffic occupies 0x0000-0x0FFF, telemetry 0x1000-0xFFFF, by
convention. Classic cFS packs an app tag in the upper bits and a
message number in the lower bits, e.g. 0x1900 = app 0x19, message 0x00.
Modern cFE (6.7+) widened message IDs to 32 bits; see
references/cfs-architecture-notes.md.
- Classic app lifecycle: every cFS app follows APP_Init, APP_Execute,
APP_Data. APP_Init runs once (register with ES, subscribe to command
message IDs); APP_Execute is the per-cycle main-loop body (route
pending SB messages, run the cycle); APP_Data processes one received
message or produces the cycle's telemetry. The loop is
APP_Main: APP_Init(); while(1) { APP_Execute(); }.
- Telemetry convention: each telemetry message carries a sequence counter
in its header (CFE_SB_TlmHdr.SeqCnt) that increments per message, so a
ground station can detect dropped packets.
- Event severity levels (CFE_EVS): DEBUG, INFO, EVENT, ERROR, CRITICAL.
Events are filtered by severity on the ground and by app at runtime;
a flooded event log is a real operational hazard on orbit.
Workflow
- Identify the layer: hardware/PSP, OSAL, cFE service, or flight app.
Board bring-up and CPU support belong to PSP; RTOS portability
belongs to OSAL; app design belongs to the cFE service layer.
- Design each app with the classic lifecycle: APP_Init for one-time
registration and subscription, APP_Execute for the per-cycle body,
APP_Data for per-message or per-cycle work.
- Allocate message IDs: telemetry in 0x1000-0xFFFF, commands in
0x0000-0x0FFF, one block per app, app tag in the upper bits.
- Model the bus in the simulation: register_app() every app,
subscribe(app, msg_id) each consumer, publish(msg_id, payload) each
producer, then route_messages() to deliver in publish order.
- Stamp telemetry sequence counters with telemetry_pipeline() so the
ground station can verify continuity.
- Log anomalies through the event service: EventLog.log(app, severity,
message), choosing DEBUG for diagnostics, INFO/EVENT for normal
milestones, ERROR for recoverable faults, CRITICAL for loss of
function.
- Verify the model: run the contract test, then check the event log
and per-app delivery lists against the expected publish order.
Worked example
A GNC app publishes attitude telemetry on a schedule; an ACS app and an
EPS app consume different message IDs from the same bus.
import cfs_architecture_logic as cfs
bus = cfs.SoftwareBus()
bus.register_app("ACS"); bus.subscribe("ACS", 0x1900)
bus.register_app("EPS"); bus.subscribe("EPS", 0x1901)
bus.publish(0x1900, {"cmd": "rate_damp"})
bus.publish(0x1901, {"cmd": "battery_charge"})
bus.publish(0x1900, {"cmd": "slew"})
bus.route_messages()
bus.deliveries("ACS") # [(ACS, 0x1900, rate_damp), (ACS, 0x1900, slew)]
bus.deliveries("EPS") # [(EPS, 0x1901, battery_charge)] in publish order
Telemetry pipeline with sequence counters:
stamped = cfs.telemetry_pipeline(bus, "GNC", 0x1902,
["quat1", "quat2", "quat3"])
# [(0x1902, "quat1", 0), (0x1902, "quat2", 1), (0x1902, "quat3", 2)]
Scheduled telemetry app (publishes every 2 cycles):
gnc = cfs.ScheduledTelemetryApp("GNC", bus, 0x1800, 0x1902, period=2)
bus.register_app("TEL"); bus.subscribe("TEL", 0x1902)
gnc.APP_Init()
for _ in range(5):
gnc.APP_Execute()
bus.route_messages() # TEL receives seq 0,1,2 at cycles 0,2,4
Verification checklist
- Every app registers exactly once; duplicate registration raises
ValueError.
- Every consumer subscribes before any publish to its message ID;
publishing to an unknown message ID raises ValueError.
- Message IDs are validated as 16-bit (0x0000-0xFFFF); out-of-range or
non-integer IDs raise ValueError.
- After route_messages(), each app's delivery list contains only its
subscribed message IDs, in publish order.
- Telemetry sequence counters are monotonic per message ID.
- Event log entries carry a valid severity; unknown severity raises
ValueError.
Pitfalls
- Confusing the cFS layers: PSP is the only board-specific layer
(CPU reset, clock, memory, console), OSAL wraps the RTOS behind one
API so apps never call it directly, and cFE provides the reusable
services - board bring-up belongs to PSP, RTOS portability to OSAL,
and app design to the cFE service layer, so routing an OSAL tasking
question to the PSP leaf-side model misses the layer.
- Allocating message IDs against the ranges: commands occupy
0x0000-0x0FFF and telemetry 0x1000-0xFFFF by convention, with the
app tag in the upper bits and the message number below (0x1900 is
app 0x19, message 0x00) - and the bus validates IDs as 16-bit, so
out-of-range or non-integer IDs raise ValueError; note modern cFE
6.7+ widened message IDs to 32 bits.
- Publishing before the consumer subscribes: every consumer must
subscribe to its message ID before any publish to that ID, and
publishing to an unknown message ID raises ValueError - a publisher
that sends before subscription silently loses the first messages
for that app.
- Misusing the app lifecycle: APP_Init runs once and does the
registration and subscription, APP_Execute is the per-cycle body
that routes pending messages, and every app registers exactly once -
duplicate registration raises ValueError, and registering or
subscribing inside the execute loop re-runs one-time work every
cycle.
- Trusting delivery order or counters without the route: after
route_messages() each app's delivery list holds only its subscribed
message IDs in publish order, and telemetry sequence counters are
monotonic per message ID via telemetry_pipeline() - the ground
station detects drops from counter gaps, so a stamped pipeline is
part of the design, not an afterthought.
- Treating the software bus as a network bus: SB routing is in-process
publish/subscribe by message ID with copies to every subscriber's
pipe - cross-box cFS traffic rides ARINC 664 / MIL-STD-1553 links
(the data-bus leaves), and ARINC 653 partitioning is the
hardware-level isolation sibling, not a message-routing service.
Behavior contract (gate 3)
The software bus routing, event log, telemetry pipeline, and app
skeleton are exercised by the contract test:
scripts/test_cfs_architecture.py against scripts/cfs_architecture_logic.py
(stdlib unittest, offline, deterministic). Run:
python3 scripts/test_cfs_architecture.py
References
- scripts/cfs_architecture_logic.py: SoftwareBus, EventLog,
telemetry_pipeline(), app_skeleton_template(),
ScheduledTelemetryApp.
- scripts/test_cfs_architecture.py: contract test, 15 cases.
- references/cfs-architecture-notes.md: cFE/OSAL/PSP facts, message ID
layout, classic vs modern cFE API notes.
Related skills
- avionics/do178c/planning: cFS flight apps are developed and
certified under DO-178C; the DAL and PSAC flow starts there.
- avionics/data-bus/arinc664-afdx and avionics/data-bus/mil-std-1553:
the software bus is in-process message passing, not a network data
bus; AFDX/1553 carry cFS messages between boxes.
- avionics/ima/ima-partitioning: ARINC 653 partitioning is the
hardware-level sibling of cFS app isolation.
Compliance
- Standards referenced, not reproduced: cFS is NASA open-source software
(Apache-2.0), not a certification standard; this leaf keys to
do-178c (the governing airborne software standard for cFS flight
apps) listed reference-only per standards-map.yaml.
- The simulation implements the classic cFE 6.x model; modern cFE API
differences are noted in references/cfs-architecture-notes.md.
- compliance: STANDARDS-REF, gated: false.
1---2name: cfs-architecture3description: Use when designing a cFS app, explaining cFS layering, or simulating software bus routing for flight software. Model and simulate NASA core Flight Software (cFS) architecture: explain the cFE/OSAL/PSP layering (Executive Services, Software Bus, Event Services, Table Services, Time Services, File Services), structure apps with the classic APP_Init, APP_Execute, APP_Data pattern, and route messages by 16-bit message ID over a software bus publish/subscribe model, with a pure-Python simulation that registers apps, subscribes to message IDs, publishes payloads, routes queued messages in publish order, stamps telemetry sequence counters, and logs events by severity. Trigger: cFS, core flight software, cFE, OSAL, PSP, software bus, publish subscribe, app skeleton, telemetry pipeline.4license: Apache-2.05---67# cFS Architecture (avionics/fsw/cfs-architecture)89Use when the task is NASA core Flight Software (cFS) architecture: the10cFE/OSAL/PSP layering, the core flight executive (cFE) applications, the11classic app lifecycle pattern, or software bus publish/subscribe routing12by 16-bit message ID. The module simulates the software bus and event13service in pure Python: you register apps, subscribe to message IDs,14publish payloads, route queued messages in publish order, stamp telemetry15sequence counters, and log events by severity.1617## Domain quick reference1819- cFS is the NASA open-source flight software framework: an application20 platform (cFE), an OS abstraction layer (OSAL), and a board support21 layer (PSP), with flight apps running on top. Layering bottom to top:22 hardware, PSP, OSAL, cFE core, flight apps. cFE 6.x is the reference23 baseline; the project is maintained on GitHub (nasa/cFS, Apache-2.0).24- PSP (Platform Support Package) is the only board-specific layer: CPU25 reset, clock/timer, memory, EEPROM, and console support. One cFE binary26 tree, one PSP per target processor.27- OSAL (OS Abstraction Layer) wraps the real-time OS (RTEMS, VxWorks,28 POSIX) behind one API: tasks, semaphores, queues, timers, mutexes,29 filesystem, network. Apps never call the RTOS directly.30- cFE (core Flight Executive) provides the reusable services:31 - Executive Services (ES): app startup/stop, memory pools, housekeeping32 catalog, reset control.33 - Software Bus (SB): publish/subscribe message routing by message ID,34 the backbone of all inter-app communication.35 - Event Services (EVS): severity-filtered event log for operator and36 FDIR visibility.37 - Table Services (TBL): loadable configuration tables with validation.38 - Time Services (TIME): time source management, time sync messages.39 - File Services (FS): filesystem utilities, file headers, file/disk40 housekeeping.41- Software bus routing: a message ID (16-bit in classic cFS) selects the42 destination. Apps subscribe to message IDs; publishers call43 CFE_SB_SendMsg; SB copies the message to every subscriber's pipe.44 Command traffic occupies 0x0000-0x0FFF, telemetry 0x1000-0xFFFF, by45 convention. Classic cFS packs an app tag in the upper bits and a46 message number in the lower bits, e.g. 0x1900 = app 0x19, message 0x00.47 Modern cFE (6.7+) widened message IDs to 32 bits; see48 references/cfs-architecture-notes.md.49- Classic app lifecycle: every cFS app follows APP_Init, APP_Execute,50 APP_Data. APP_Init runs once (register with ES, subscribe to command51 message IDs); APP_Execute is the per-cycle main-loop body (route52 pending SB messages, run the cycle); APP_Data processes one received53 message or produces the cycle's telemetry. The loop is54 APP_Main: APP_Init(); while(1) { APP_Execute(); }.55- Telemetry convention: each telemetry message carries a sequence counter56 in its header (CFE_SB_TlmHdr.SeqCnt) that increments per message, so a57 ground station can detect dropped packets.58- Event severity levels (CFE_EVS): DEBUG, INFO, EVENT, ERROR, CRITICAL.59 Events are filtered by severity on the ground and by app at runtime;60 a flooded event log is a real operational hazard on orbit.6162## Workflow63641. Identify the layer: hardware/PSP, OSAL, cFE service, or flight app.65 Board bring-up and CPU support belong to PSP; RTOS portability66 belongs to OSAL; app design belongs to the cFE service layer.672. Design each app with the classic lifecycle: APP_Init for one-time68 registration and subscription, APP_Execute for the per-cycle body,69 APP_Data for per-message or per-cycle work.703. Allocate message IDs: telemetry in 0x1000-0xFFFF, commands in71 0x0000-0x0FFF, one block per app, app tag in the upper bits.724. Model the bus in the simulation: register_app() every app,73 subscribe(app, msg_id) each consumer, publish(msg_id, payload) each74 producer, then route_messages() to deliver in publish order.755. Stamp telemetry sequence counters with telemetry_pipeline() so the76 ground station can verify continuity.776. Log anomalies through the event service: EventLog.log(app, severity,78 message), choosing DEBUG for diagnostics, INFO/EVENT for normal79 milestones, ERROR for recoverable faults, CRITICAL for loss of80 function.817. Verify the model: run the contract test, then check the event log82 and per-app delivery lists against the expected publish order.8384## Worked example8586A GNC app publishes attitude telemetry on a schedule; an ACS app and an87EPS app consume different message IDs from the same bus.8889```python90import cfs_architecture_logic as cfs9192bus = cfs.SoftwareBus()93bus.register_app("ACS"); bus.subscribe("ACS", 0x1900)94bus.register_app("EPS"); bus.subscribe("EPS", 0x1901)9596bus.publish(0x1900, {"cmd": "rate_damp"})97bus.publish(0x1901, {"cmd": "battery_charge"})98bus.publish(0x1900, {"cmd": "slew"})99bus.route_messages()100101bus.deliveries("ACS") # [(ACS, 0x1900, rate_damp), (ACS, 0x1900, slew)]102bus.deliveries("EPS") # [(EPS, 0x1901, battery_charge)] in publish order103```104105Telemetry pipeline with sequence counters:106107```python108stamped = cfs.telemetry_pipeline(bus, "GNC", 0x1902,109 ["quat1", "quat2", "quat3"])110# [(0x1902, "quat1", 0), (0x1902, "quat2", 1), (0x1902, "quat3", 2)]111```112113Scheduled telemetry app (publishes every 2 cycles):114115```python116gnc = cfs.ScheduledTelemetryApp("GNC", bus, 0x1800, 0x1902, period=2)117bus.register_app("TEL"); bus.subscribe("TEL", 0x1902)118gnc.APP_Init()119for _ in range(5):120 gnc.APP_Execute()121bus.route_messages() # TEL receives seq 0,1,2 at cycles 0,2,4122```123124## Verification checklist125126- Every app registers exactly once; duplicate registration raises127 ValueError.128- Every consumer subscribes before any publish to its message ID;129 publishing to an unknown message ID raises ValueError.130- Message IDs are validated as 16-bit (0x0000-0xFFFF); out-of-range or131 non-integer IDs raise ValueError.132- After route_messages(), each app's delivery list contains only its133 subscribed message IDs, in publish order.134- Telemetry sequence counters are monotonic per message ID.135- Event log entries carry a valid severity; unknown severity raises136 ValueError.137138## Pitfalls139140- Confusing the cFS layers: PSP is the only board-specific layer141 (CPU reset, clock, memory, console), OSAL wraps the RTOS behind one142 API so apps never call it directly, and cFE provides the reusable143 services - board bring-up belongs to PSP, RTOS portability to OSAL,144 and app design to the cFE service layer, so routing an OSAL tasking145 question to the PSP leaf-side model misses the layer.146- Allocating message IDs against the ranges: commands occupy147 0x0000-0x0FFF and telemetry 0x1000-0xFFFF by convention, with the148 app tag in the upper bits and the message number below (0x1900 is149 app 0x19, message 0x00) - and the bus validates IDs as 16-bit, so150 out-of-range or non-integer IDs raise ValueError; note modern cFE151 6.7+ widened message IDs to 32 bits.152- Publishing before the consumer subscribes: every consumer must153 subscribe to its message ID before any publish to that ID, and154 publishing to an unknown message ID raises ValueError - a publisher155 that sends before subscription silently loses the first messages156 for that app.157- Misusing the app lifecycle: APP_Init runs once and does the158 registration and subscription, APP_Execute is the per-cycle body159 that routes pending messages, and every app registers exactly once -160 duplicate registration raises ValueError, and registering or161 subscribing inside the execute loop re-runs one-time work every162 cycle.163- Trusting delivery order or counters without the route: after164 route_messages() each app's delivery list holds only its subscribed165 message IDs in publish order, and telemetry sequence counters are166 monotonic per message ID via telemetry_pipeline() - the ground167 station detects drops from counter gaps, so a stamped pipeline is168 part of the design, not an afterthought.169- Treating the software bus as a network bus: SB routing is in-process170 publish/subscribe by message ID with copies to every subscriber's171 pipe - cross-box cFS traffic rides ARINC 664 / MIL-STD-1553 links172 (the data-bus leaves), and ARINC 653 partitioning is the173 hardware-level isolation sibling, not a message-routing service.174175## Behavior contract (gate 3)176177The software bus routing, event log, telemetry pipeline, and app178skeleton are exercised by the contract test:179scripts/test_cfs_architecture.py against scripts/cfs_architecture_logic.py180(stdlib unittest, offline, deterministic). Run:181python3 scripts/test_cfs_architecture.py182183## References184185- scripts/cfs_architecture_logic.py: SoftwareBus, EventLog,186 telemetry_pipeline(), app_skeleton_template(),187 ScheduledTelemetryApp.188- scripts/test_cfs_architecture.py: contract test, 15 cases.189- references/cfs-architecture-notes.md: cFE/OSAL/PSP facts, message ID190 layout, classic vs modern cFE API notes.191192## Related skills193194- avionics/do178c/planning: cFS flight apps are developed and195 certified under DO-178C; the DAL and PSAC flow starts there.196- avionics/data-bus/arinc664-afdx and avionics/data-bus/mil-std-1553:197 the software bus is in-process message passing, not a network data198 bus; AFDX/1553 carry cFS messages between boxes.199- avionics/ima/ima-partitioning: ARINC 653 partitioning is the200 hardware-level sibling of cFS app isolation.201202## Compliance203204- Standards referenced, not reproduced: cFS is NASA open-source software205 (Apache-2.0), not a certification standard; this leaf keys to206 do-178c (the governing airborne software standard for cFS flight207 apps) listed reference-only per standards-map.yaml.208- The simulation implements the classic cFE 6.x model; modern cFE API209 differences are noted in references/cfs-architecture-notes.md.210- compliance: STANDARDS-REF, gated: false.