The history and design of the TcMatrix library, a TwinCAT library for matrix and vector representation and computation in the structured text language.
Background
PLCs are good at a lot of things, but complex mathematical computation is not one of them. Matrix algebra is very common in a lot of academic control theory but is rarely used in PLCs directly (motion control and other complex tasks are usually handled in vendor C libraries not IEC-61131 languages). For some reason I kept finding myself in need of matrix algebra features for different commercial projects, so the first time it happened as a freelancer I worked off the clock for a week to bang out a thorough, well-tested library so I would never have to hack my way through it again.
Ground Based Tracking Antennas
The first time I found myself needing to do matrix algebra on a PLC was while working on the first SpaceX Starlink gateway reflector antenna prototype. I needed to point the antenna to within a half degree of the Tintin test satellites that were traveling at over 17,000 MPH up above and I was using a Beckhoff PLC to control the two-axis ground antenna system. The PC side had a script to pull up to date ephemeris data (time, latitude, longitude, altitude) from the corporate network as a CSV file before each pass. The CSV would get loaded into a data block (DB) on the PLC, but the PLC needed a function to figure out the azimuth and elevation to point towards for any given satellite position.

I elected to use homogenous transformation matrices (4×4) to shift information between all of the different coordinate systems (ECEF for the satellite, tangent ENU for the ground antenna, and a few other intermediate coordinate systems with calibration offsets). Unfortunately, the few available math libraries (notable some features from OSCAT) only had support for 3×3 matrices and did not include tools for matrix inversion. I hacked together a few FB’s to act as specialized classes for vectors, transformation matrices, and other specific steps of my applications workflow – after all, this was just a prototype!
The prototype worked well, but a few months later I was trying to write production code for a Siemens S7-1200 PLC to do something very similar. Siemens had the beginnings of a matrix math library available, but it was still not complete enough for what I needed to accomplish. I mostly copied over the specialized FB code from TwinCAT to TIA Portal (thank you IEC language standards!) and added/cleaned features as necessary to meet the new requirements. This was my second experience with matrix algebra on a PLC and I was hoping it was my last!
Precise Outdoor Installation
My first project as a freelance engineer was a 3-DOF cartesian mechanism mounted to the back of a tractor that needed to embed posts at precise positions in the ground of a construction site. The tractor had a dual-antenna GNS system which could be combined with axis feedback to determine the absolute position of the end effector and posts. Once again, I found myself with a series of coordinate transformations that I would need to manage within the control loop of a PLC.

I swore that this time would be my last, so with the blessing of my client I clocked out and worked for a week on my own to make the first version of TcMatrix, the open source TwinCAT structured text matrix and vector library. A secondary motivation for creating the library was to try out the unit testing suite TcUnit, something that is rare to use in the PLC world and that I had not had the opportunity to try before.
When I needed a quick data logging to CSV utility I found that it was very easy to extend the functionality of the TcMatrix library to include CSV file read and write features. I eventually added TcTransform as an optional layer with a ton of helper functions to assist with 3 and 4 rank rotation matrices, along with a TcGeodetic library for latitude/longitude/altitude conversions and other ellipsoid body math.
Library Design
There were several major design decisions involved with the development of the library. Although the library has evolved somewhat from its initial barebones feature set, these initial design decisions have had immense impact on the structure and use of the code.
Memory Management: Static vs Dynamic
The biggest design decision in the TcMatrix library is where the memory for the data within the matrix should live. Other low-level libraries (such as the C++ boost linear algebra library) give developers the option between heap (dynamic) and stack (static) allocations. While TwinCAT supports heap allocation with the __NEW operator, it is only an extension to the 61131-3 standard and the practice is very frowned upon (not to mention tricky to implement with online code changes). However there are a few use cases where dynamic allocation at initialization only (not recurring cyclically) can make derived code more reusable.
In the end I opted to make a base abstract Matrix class (FB) and provide multiple concrete implementations with different memory options:
- Dynamic: using __NEW and an internal pointer to a 1D data buffer
- Static external: providing 1D pointer to external data buffer, with max length
- Static external: providing existing 2D ARRAY via VAR_IN_OUT
- Static internal (abstract): deriving FB must provide data to parent FB
To accomplish this, the base Matrix class has virtual functions that are used to get or set the value of data at a particular row and column of the matrix. This creates a significant performance impact, but people doing matrix algebra in an IEC language (instead of C/C++) are probably more concerned about convenience than performance (I know I was!). There may be an additional layer of abstraction where a derived class that uses 1D data storage transforms a row and column index into a 1D index.
Matrix Accessors: Non-Copy Transformations
There are many common operations on an array that involve simply rearranging the contents of the array or accessing a subset instead of changing any of the individual values. Making a copy of the data within a matrix for simple reads (like reading a specific column of a matrix as a vector, or excluding a specific row/column of a matrix to read a sub-matrix) can use up scarce stack memory very quickly (especially for recursive calculations like the determinant).
The solution I came up with to this problem is “Matrix Accessor” FB’s that present themselves as a Matrix but can translate the row and column indexes automatically to properly reference data from the source FB. This type of matrix “view” was easy to implement because the base Matrix FB already has provisions for a variety of memory sources. While more can be imagined, the three types that are included in the library are:
- Transpose: Interprets an existing matrix as its transposed form
- SubMatrix: Interpret a contiguous subset of an existing matrix as its own, smaller matrix
- ShrunkMatrix: Interpret an existing matrix as a smaller one where exactly one row and column have been remoted (very handy for determinant calculations)
Matrix Functions
Because some concrete Matrix FBs include internal pointers to external memory and TwinCAT does not give you access to override or react to “copy on assignment” expressions all of the matrix operations and functions were designed as external functions (FC’s) that require providing a result storage variable as an input (VAR_IN_OUT). The Matrix FB itself has plenty of helper methods, but they merely call the external independent functions (sometimes with ‘THIS’ FB as an input to the function, and sometimes with the ‘THIS’ FB as the result of the function!)
The actual mathematical functions are fairly trivial, mostly just scalar or element-wise arithmetic. The Product, Invert (square only) and Determinant (square only) functions are the only complex ones. All of the functions have checks to ensure the ranks/sizes of the various input and resultant matrices are compatible.
There are a few vector functions included, although the library makes no structural differentiation between a matrix and a vector. These functions simply check that the inputs are one dimensional the same way that other matrix operations might need to confirm size and rank compatibility.

