In this chapter, we’ll explore some of the more advanced features of the Ada programming language and how you can leverage these in the context of developing aerospace software. The topics covered will include Access Types and Pointers, File and I/O Stream operations, and Interfacing with other languages.
Access Types and Pointers in Ada Programming Language
Access types are Ada’s equivalent to pointers found in languages like C/C++. They are used to reference objects or data, and to build dynamic and complex data structures such as linked lists, trees, and graphs. Despite their similarities to pointers, access types in Ada have certain advantages that enhance safety and prevent common programming pitfalls.
Here’s how you can declare an access type:
type Node_Access is access all Node;
In this case, Node_Access is an access type that points to a Node object. An object of this type can point to any Node object.
The ‘Access attribute is used to create access values:
Node_Ptr : Node_Access; Node_Obj : aliased Node; Node_Ptr := Node_Obj'Access;
Here, Node_Obj is an aliased object of type Node, and Node_Ptr is an access value that points to Node_Obj.
An important feature of Ada’s access types is the automatic garbage collection. This is where Ada sets itself apart from languages like C and C++. In Ada, objects that are allocated dynamically and are no longer accessible are automatically deallocated, preventing memory leaks.
When working in the context of aerospace software, access types can be highly beneficial. For instance, consider a flight management system that needs to keep track of a dynamically changing list of waypoints. With access types, you could create a linked list data structure to hold these waypoints, adding and removing waypoints as needed during the flight.
Here’s a simple example of how you might define a linked list node for such a system:
type Waypoint is record Longitude : Float; Latitude : Float; end record; type Waypoint_Node; type Waypoint_Node_Access is access Waypoint_Node; type Waypoint_Node is record Data : Waypoint; Next : Waypoint_Node_Access; end record;
In this example, Waypoint_Node_Access is an access type that points to a Waypoint_Node. This allows you to create a linked list of Waypoint_Node objects, with each node holding a Waypoint and a pointer to the next node in the list.
Access types are a powerful tool in Ada’s arsenal, providing the flexibility of pointers while mitigating many of the risks. When used effectively, they can be a key component in writing robust, high-integrity aerospace software.
Example
Access types can be used to create complex data structures like linked lists or trees. In the context of a Flight Management System (FMS), let’s consider we want to represent a flight plan as a linked list of waypoints. Here’s how we could implement that in Ada.
First, let’s define a Waypoint type:
type Waypoint is record Latitude : Float; Longitude : Float; Altitude : Float; end record;
Next, we define our linked list. Each node in the list will have a Waypoint and a reference to the next node. The last node in the list will have null as its next node.
type Waypoint_Node; type Waypoint_Node_Access is access Waypoint_Node; type Waypoint_Node is record Data : Waypoint; Next : Waypoint_Node_Access := null; end record;
Now, let’s define a Flight_Plan type that will represent our linked list of waypoints.
type Flight_Plan is record Head : Waypoint_Node_Access := null; end record;
Now we can define procedures to add and remove waypoints to/from the flight plan:
procedure Add_Waypoint(Plan : in out Flight_Plan; W : Waypoint) is New_Node : Waypoint_Node_Access := new Waypoint_Node'(Data => W, Next => null); begin if Plan.Head = null then Plan.Head := New_Node; else declare Current : Waypoint_Node_Access := Plan.Head; begin while Current.Next /= null loop Current := Current.Next; end loop; Current.Next := New_Node; end; end if; end Add_Waypoint; procedure Remove_Waypoint(Plan : in out Flight_Plan) is begin if Plan.Head /= null then Plan.Head := Plan.Head.Next; end if; end Remove_Waypoint;
The Add_Waypoint procedure adds a waypoint at the end of the flight plan, and Remove_Waypoint removes the first waypoint.
Finally, here’s how you might use this in a FMS:
procedure Use_Flight_Plan is Plan : Flight_Plan; begin Add_Waypoint(Plan, (Latitude => 38.9072, Longitude => -77.0369, Altitude => 0.0)); -- Washington, D.C. Add_Waypoint(Plan, (Latitude => 34.0522, Longitude => -118.2437, Altitude => 0.0)); -- Los Angeles, CA Remove_Waypoint(Plan); -- We're done with Washington, D.C., remove it from the plan end Use_Flight_Plan;
In this example, we have implemented a simple flight plan as a linked list of waypoints using access types. The flight plan starts at Washington, D.C. and ends at Los Angeles, CA. Once the plane has reached Washington, D.C., we remove it from the flight plan.
Working with Files and I/O Streams
Ada provides robust support for file and I/O operations through packages like Ada.Text_IO and Ada.Directories.
Here’s an example of reading from a file in Ada:
with Ada.Text_IO; procedure Read_File is File : Ada.Text_IO.File_Type; begin Ada.Text_IO.Open(File, Mode => Ada.Text_IO.In_File, Name => "waypoints.txt"); while not Ada.Text_IO.End_Of_File(File) loop Ada.Text_IO.Put_Line(Item => Ada.Text_IO.Get_Line(File)); end loop; Ada.Text_IO.Close(File); end Read_File;
In this example, Ada opens a file named “waypoints.txt” and reads it line by line until it reaches the end of the file. This kind of operation might be used in an FMS to load a list of waypoints from a file.
Example
The Ada programming language includes a set of predefined input-output packages that allow us to easily read and write files. In this section, we will show how we could use these packages to write flight data to a file in the context of an aerospace software system.
First, we need to with the Ada.Text_IO and Ada.Float_Text_IO packages at the beginning of our code:
with Ada.Text_IO; use Ada.Text_IO; with Ada.Float_Text_IO; use Ada.Float_Text_IO;
Next, let’s define a Waypoint type and a Flight_Plan array of waypoints:
type Waypoint is record Latitude : Float; Longitude : Float; Altitude : Float; end record; type Flight_Plan is array (1 .. 10) of Waypoint;
Then, we will define a procedure that writes a Flight_Plan to a file:
procedure Write_Flight_Plan(Plan : Flight_Plan; Filename : in String) is File : File_Type; begin Create(File, Out_File, Filename); for WP in Plan loop Put(File, WP.Latitude, Fore => 1, Aft => 6, Exp => 0); Put(File, ", "); Put(File, WP.Longitude, Fore => 1, Aft => 6, Exp => 0); Put(File, ", "); Put(File, WP.Altitude, Fore => 1, Aft => 6, Exp => 0); New_Line(File); end loop; Close(File); end Write_Flight_Plan;
When you run this program, it will create a file named “flight_plan.txt” with the following content:
37.774900, -122.419400, 0.000000 34.052200, -118.243700, 0.000000 0.000000, 0.000000, 0.000000 ...
In this example, we’ve shown how to write structured data to a text file in Ada. The principles shown here can be extended to write more complex data to files, or to read data from files.
Interfacing with Other Languages (C, Python, etc.)
One of the key aspects of Ada that makes it a versatile language is its capability to interface with other programming languages. This is especially useful when integrating Ada with legacy systems, leveraging libraries written in other languages, or when some parts of a system are better suited to a specific language. The most common language Ada interfaces with is C, but it can also interface with languages such as Python, Java, and others.
Interfacing Ada with C
Ada can interface directly with C and, in fact, Ada’s standard calling conventions are compatible with those of C. To access a C function from Ada, you simply need to declare the function with the Import aspect and the Convention aspect set to C. For example, if you had a C function declared like this:
// C code double calculateDistance(double lat1, double lon1, double lat2, double lon2);
You could access it from Ada like this:
-- Ada code function Calculate_Distance(Lat1, Lon1, Lat2, Lon2 : Float) return Float with Import, Convention => C, External_Name => "calculateDistance";
Interfacing Ada with Python
Interfacing Ada with Python is less straightforward, as Python is a high-level, interpreted language. One common approach is to use the Python C API to create a C layer between Ada and Python. Ada can then interface with this C layer in the same way as shown above. This can be complex and involves a good understanding of both Python and C.
Another approach is to use an Ada-Python binding library. These libraries provide Ada bindings to the Python C API, simplifying the process of interfacing the two languages. However, as of my knowledge cutoff in September 2021, there isn’t a widely-used Ada-Python binding library.
Regardless of the language you’re interfacing Ada with, it’s important to remember that Ada’s strong typing and strict checks may not always map directly to constructs in the other language. Therefore, you need to be careful to ensure that data is correctly converted and handled when crossing the language boundary.
That being said, it’s not impossible, but would require a few extra steps. A common method of interfacing between Ada and Python would involve the use of the Python’s C API to create a C layer that Ada can interact with.
Here’s a simple example of how you might interface Ada with Python via a C layer:
Firstly, in Python, you might have a function in a file called calc.py:
# Python code (calc.py) def add_numbers(a, b): return a + b
Then, you would need to create a C wrapper for this Python function:
// C code #include <Python.h> double c_add_numbers(double a, double b) { PyObject *pName, *pModule, *pFunc; PyObject *pArgs, *pValue; double result = 0; Py_Initialize(); pName = PyUnicode_FromString("calc"); pModule = PyImport_Import(pName); Py_DECREF(pName); if (pModule != NULL) { pFunc = PyObject_GetAttrString(pModule, "add_numbers"); if (pFunc && PyCallable_Check(pFunc)) { pArgs = PyTuple_New(2); PyTuple_SetItem(pArgs, 0, PyFloat_FromDouble(a)); PyTuple_SetItem(pArgs, 1, PyFloat_FromDouble(b)); pValue = PyObject_CallObject(pFunc, pArgs); Py_DECREF(pArgs); if (pValue != NULL) { result = PyFloat_AsDouble(pValue); Py_DECREF(pValue); } } Py_DECREF(pFunc); Py_DECREF(pModule); } Py_Finalize(); return result; }
Finally, in Ada, you can interface with the C function like this:
-- Ada code with Interfaces.C; use Interfaces.C; with Ada.Text_IO; use Ada.Text_IO; procedure Add_Numbers is function C_Add_Numbers(A, B : C_Double) return C_Double with Import, Convention => C, External_Name => "c_add_numbers"; function To_C(Double : Float) return C_Double is begin return C_Double(Double); end To_C; function To_Ada(Double : C_Double) return Float is begin return Float(Double); end To_Ada; A : Float := 3.5; B : Float := 2.3; Result : Float; begin Result := To_Ada(C_Add_Numbers(To_C(A), To_C(B))); Put_Line(Float'Image(Result)); end Add_Numbers;
This Ada code interfaces with the C function c_add_numbers, which in turn calls the Python function add_numbers.
Please note that this is a simple illustration. Real-world interfacing would be much more complex and would require thorough error checking and handling. You would also need to handle the Python interpreter’s initialization and finalization, which may not be as simple as in the C example above if the Ada application is long-lived or multi-threaded.
As a final note, this level of cross-language interfacing may impact the performance, safety, and reliability benefits that Ada typically provides, so it should only be used when necessary and with full understanding of the implications.
This post was published by Admin.
Email: admin@TheCloudStrap.Com