Engineering Insights
Software Engineering
Programming Languages for ATM Systems
Rust Lang

Programming Languages for ATM Systems

Introduction

⚠️ This post is an early draft; expect updates.

The primary objective of Air Traffic Management (ATM) systems is to guarantee separation between aircraft. As the European airspace becomes more congested and traffic volumes continue to rise, ATM systems face growing pressure to safely manage operational data while responding in real time to thousands of concurrent events with minimal latency. Because a loss of separation can result in serious hazards, ATM systems fall squarely within the safety-critical systems domain.

This post is the first in a series exploring software engineering best practices for implementing safety-critical ATM systems. It introduces the general concepts behind safe programming languages, while subsequent posts will examine specific languages such as C++, Ada, and Rust. The intended audience for this post includes:

  • Software engineers seeking a deeper understanding of safety-critical ATM systems.
  • Organizations aiming to become an approved EASA Design or Production Organization (DPO).

An Overview of The Surveillance Chain

To illustrate this post, let us consider the typical, albeit simplified, en-route surveillance system depicted in Figure 1 below (a production-grade surveillance system is, of course, far more complex and involves many additional subsystems).

Rust Lang

Figure 1: Simplified en-route surveillance chain.

The surveillance system depicted in Figure 1 comprises multiple sensors used to track an aircraft's position.

The result of the processing chain is ultimately rendered by the situation display client, colloquially referred to as the "radar display," and used by air traffic controllers to provide clearances (see Figure 2 below).

Rust Lang

Figure 2: The situation display client, commonly referred to as the “radar display,” used by air traffic controllers to issue clearances (image source: Eurocontrol).

Programming Languages for Safety-Critical Systems

Considering the ATM surveillance system outlined in the previous section, the choice of the programming
languages for implementing the various sub-systems is not merely a technical decision — it is central to building resilient, mission-critical, and safety-critical ATM systems. In other words, while programming language selection often provokes strong opinions — sometimes bordering on religious wars — In the context of ATM systems, it must be guided first and foremost by safety considerations.

Before diving further into programming languages, it is worthwhile clarifying the distinction between mission-critical and safety-critical systems:

  • Mission-critical systems are those whose failure would prevent an organization from achieving its essential objectives. Downtime or errors may cause severe disruption or financial loss, but there is no immediate danger to human life. In the ATM domain, a Flight Data Processing (FDP) system is a good example of a mission-critical system. Its failure will cause severe operational disruptions, but it does not directly endanger human life

  • Safety-critical systems are those whose failure could directly result in injury, loss of life, or significant environmental harm. Again, in the ATM domain, a Surveillance Data Processing System (SDPS) is safety-critical: a failure or design flaw could lead to a loss of separation between aircraft, creating a real risk of collision.

What makes a Programming Language "Safe"?

No programming language is inherently safe by default. As illustrated below, a “safe language” typically refers to a restricted safe subset of the language, specified by coding standards that define the permitted constructs and syntax to constrain program behavior — for example, prohibiting recursion, disallowing self-modifying code, or enforcing limits such as a maximum number of lines per function.

Rust Lang

Figure 2: Reliable Software is built using a language subset specified by coding standards, and the accompanying verification toolset

The coding standards must be also paired with an accompanying toolset — including static analysis tools, verification tools and qualified compilers. The ultimate goal is to enforce a structured approach to specifying, implementing, and verifying software (and the system as a whole), ensuring that the final system demonstrably meets stringent safety, reliability, and assurance levels.

Memory Safety

According to Microsoft (opens in a new tab), a large number of security vulnerabilities can be traced back to manual memory management errors. Putting security vulnerabilities aside, ATM systems are critical systems where software crashes due to memory management errors is simply not an option.

Some common risks associated with manual memory management include:

  • Buffer overflows — out-of-bounds writes that corrupt adjacent memory
  • Dangling pointer dereferences — accessing the address of an object after it has been freed
  • Null pointer dereferences — when accessing an object that has not been correctly initialized
  • Double free — releasing the exact memory location more than once, leading to corruption or crashes
  • Memory leak — failing to return memory, which eventually results in memory exhaustion

Memory safety is a characteristic of programming languages where all memory access is well-defined. For example, C++ is widely regarded as an "unsafe" language because it lacks built-in mechanisms to prevent the types of programming errors outlined above. Nevertheless, as will be discussed in a subsequent post dedicated to C++, robust practices and modern language constructs can significantly mitigate the risks of memory corruption.

On the other hand, languages such as Java implement memory safety by preventing manual memory management altogether and by introducing automatic mechanisms for reclaiming unused memory through a garbage collector. Unfortunately, garbage collectors also introduce non-determinism (since the exact timing of memory reclamation is unknown), which can be an issue in safety-critical systems, where unpredictable garbage collection pauses may lead to latency spikes that violate real-time constraints.

Concurrency and Thread-safety

Concurrent programming refers to different parts of a program executing independently, while parallel programming involves executing multiple parts at the same time. Whenever a program runs tasks concurrently or in parallel, a new class of issues, often hard to diagnose, can arise. In practice, concurrency is implemented using threads, which opens the door to a whole set of issues described next.

  • Race conditions — when the outcome depends on the timing or order of execution.
  • Deadlocks — when two or more tasks wait indefinitely for each other’s resources.
  • Data corruption — when multiple threads or tasks modify shared data without proper synchronization.

Architecture and Modularity

Another essential aspect of ATM systems — and of complex systems in general — is modularity. This means the ability to decompose the system into independent, testable modules that interact through well-defined interfaces. Some languages, such as Rust, encourage modularity through constructs like crates. Others, such as C++, lack a native concept of modules altogether.

  • The modular monolith
  • Cohesion
  • Coupling
  • Testability

Toolsets

  • Static analysis tools
  • Verification tools
  • Qualified compilers (ensuring that the compiler generates the intended code)

Epilogue: Managed Code

tbd