Transformations

This chapter describes all electrical transformations implemented in the VSlib.

General interface

All of the classes described below derive from and implement the Component interface. They all have a single access method, called transform. The number of arguments each implementation takes depends on the use-case, as well as the returned structure may vary. This method is intended to perform transformation on the set of values at a point of time rather than arrays or vectors of values in one execution.

abc to dq0 transformation

AbcToDq0Transform implements the from three time-domain signals of a three-phase system (abc) to an dq0 reference frame. The algorithm follows the implementation and nomeclature of the Park transformation matlab implementation.

The transform method takes four obligatory and one optional double-type arguments, one for each phase, the \(theta\) (\(=\omega t\)) angle in radians between the q and a axes, and an optional offset (\(\phi\)) in radians. The offset can be used to change the default alignment from q and a axis alignment to d and a by setting the offset to be equal to \(\frac{\pi}{2}\). The method returns a tuple of d, q, zero values. The transformation algorithm is as follows:

\[\begin{split}d &= \frac{2}{3} \left( a \cdot sin(\theta + \phi) + b \cdot sin \left( \theta + \phi - \frac{2}{3} \pi \right) + c \cdot sin \left(\theta + \phi + \frac{2}{3} \pi \right) \right) \\ q &= \frac{2}{3} \left(a \cdot cos(\theta + \phi) + b \cdot cos \left(\theta + \phi - \frac{2}{3} \pi \right) + c \cdot cos \left(\theta + \phi + \frac{2}{3} \pi \right) \right) \\ zero &= \frac{1}{3} \left( a + b + c \right) \\\end{split}\]

However, during benchmarking it was found that due to the overhead of the look-ups of the sine and cosine functions, it is preferable to perform the abc to dq0 transformation in two steps:

  1. Transform abc to \(\alpha \beta 0\) frame, and then

  2. Transform \(\alpha\beta 0\) to dq0.

For more details regarding the API, see the API documentation for AbcToDq0Transform.

Usage example

#include <numbers>

#include "abcToDq0Transform.h"
#include "rootComponent.h"

using namespace vslib;

void your_function(RootComponent& root)
{
    AbcToDq0Transform abc_to_dq0("abc_to_dq0", root);
    // no Parameter needs setting

    const double i_a    = 1.0;
    const double i_b    = -0.5;
    const double i_c    = -0.5;
    const double theta  = 0.0;
    const double offset = std::numbers::pi / 2.0;

    // no offset, q and a alignment:
    auto [d_1, q_1, zero_1]   = abc_to_dq0.transform(i_a, i_b, i_c, theta);

    // 90 degrees offset, d and a alignment:
    auto [d_2, q_2, zero_2]   = abc_to_dq0.transform(i_a, i_b, i_c, theta, offset);
}

Example usage in a vloop:

#include "vslib.hpp"

namespace fgc::user
{
    class Converter : public vslib::RootComponent
    {
    public:
        Converter() noexcept
        : vslib::RootComponent("example"),
          interrupt_1("stg", *this, 128, vslib::InterruptPriority::high, RTTask),
          abc_to_dq0("abc_to_dq0", *this)
        {
        }

        // Define your interrupts here
        vslib::PeripheralInterrupt<Converter> interrupt_1;

        // Define your public Components here
        vslib::AbcToDq0Transform abc_to_dq0;

        void init() override
        {
            interrupt_1.start();
        }

        void backgroundTask() override
        {
        }

        static void RTTask(Converter& converter)
        {
            // Read the input 3-phase voltage values:
            const double v_a    = converter.m_data[0];
            const double v_b    = converter.m_data[1];
            const double v_c    = converter.m_data[2];
            const double theta  = converter.m_data[3];
            const double offset = converter.m_data[4];

            // no offset, q and a alignment:
            auto [d_1, q_1, zero_1]   = abc_to_dq0.transform(v_a, v_b, v_c, theta);

            // 90 degrees offset, d and a alignment:
            auto [d_2, q_2, zero_2]   = abc_to_dq0.transform(v_a, v_b, v_c, theta, offset);
        }

        private:
            // actual source of data omitted for simplicity
            std::array<double, 5> m_data{0.0};
    };
}   // namespace fgc::user

dq0 to abc transformation

Dq0ToAbcTransform implements the transformation from dq0 reference frame to three-phase system (abc) dq0, an inverse of AbcToDq0Transform. The algorithm follows the implementation and nomeclature of the Inverse Park transformation Matlab implementation.

The transform method takes four obligatory and one optional double-type arguments, one for each dq0 component, the \(theta\) (\(=\omega t\)) angle in radians between the q and a axes, and an optional offset in radians. The offset can be used to change the default alignment from q and a axis alignment to d and a by setting the offset to be equal to \(\frac{\pi}{2}\). The method returns a tuple of a, b, and c values. The transformation algorithm is as follows:

\[\begin{split}a &= d \cdot sin(\theta + \phi) + q \cdot cos(theta + \phi) + zero \\ b &= d \cdot sin(\theta + \phi - \frac{2}{3} \pi) + q \cdot cos(\theta + \phi - \frac{2}{3} \pi) + zero; \\ c &= d \cdot sin(\theta + \phi + \frac{2}{3} \pi) + q \cdot cos(\theta + \phi + \frac{2}{3} \pi) + zero; \\\end{split}\]

However, during benchmarking it was found that due to the overhead of the look-ups of the sine and cosine functions, it is preferable to perform the dq0 to abc transformation in two steps:

  1. Transform dq0 to \(\alpha\beta 0\) frame, and then

  2. Transform \(\alpha\beta 0\) to abc.

For more details regarding the API, see the API documentation for Dq0ToAbcTransform.

Usage example

#include <numbers>

#include "dq0ToAbcTransform.h"
#include "rootComponent.h"

using namespace vslib;

void your_function(RootComponent& root)
{
    Dq0ToAbcTransform dq0_to_abc("dq0_to_abc", root);
    // no Parameter needs setting

    const double d      = 1.0;
    const double q      = 0.05;
    const double zero   = 0.05;
    const double theta  = 0.0;
    const double offset = std::numbers::pi / 2.0;

    // no offset, q and a alignment:
    auto [a_1, b_2, c_2]   = dq0_to_abc.transform(d, q, zero, theta);

    // 90 degrees offset, d and a alignment:
    auto [a_2, b_2, c_2]   = dq0_to_abc.transform(d, q, zero, theta, offset);
}

Example usage in a vloop:

#include "vslib.hpp"

namespace fgc::user
{
    class Converter : public vslib::RootComponent
    {
    public:
        Converter() noexcept
        : vslib::RootComponent("example"),
          interrupt_1("stg", *this, 128, vslib::InterruptPriority::high, RTTask),
          dq0_to_abc("dq0_to_abc", *this)
        {
        }

        // Define your interrupts here
        vslib::PeripheralInterrupt<Converter> interrupt_1;

        // Define your public Components here
        vslib::Dq0ToAbcTransform dq0_to_abc;

        void init() override
        {
            interrupt_1.start();
        }

        void backgroundTask() override
        {
        }

        static void RTTask(Converter& converter)
        {
            // Read the input 3-phase voltage values:
            const double d    = converter.m_data[0];
            const double q    = converter.m_data[1];
            const double zero    = converter.m_data[2];
            const double theta  = converter.m_data[3];
            const double offset = converter.m_data[4];

            // no offset, q and a alignment:
            auto [a_1, b_2, c_2]   = dq0_to_abc.transform(d, q, zero, theta);

            // 90 degrees offset, d and a alignment:
            auto [a_2, b_2, c_2]   = dq0_to_abc.transform(d, q, zero, theta, offset);
        }

        private:
            // actual source of data omitted for simplicity
            std::array<double, 5> m_data{0.0};
    };
}   // namespace fgc::user

abc to alpha-beta transformation

AbcToAlphaBetaTransform implements the abc to \(\alpha\beta0\) (Clarke) transformation from three-phase components in the abc reference frame to the rotating \(\alpha\beta0\) frame. The algorithm follows the implementation and nomeclature of the Inverse Clarke Matlab implementation.

The transform method takes three obligatory double-type arguments, one for each a, b, and c component in the abc frame of reference. The method returns a tuple of \(\alpha\), \(\beta\), and 0 values. The calculation is as follows:

\[\begin{split}\alpha &= \frac{2}{3} \left( a - \frac{b+c}{2} \right) \\ \beta &= \frac{\sqrt{3}}{3} (b - c) \\ zero &= \frac{1}{3} (a + b + c)\end{split}\]

For more details regarding the API, see the API documentation for AbcToAlphaBetaTransform.

Usage example

#include "abcToAlphaBetaTransform.h"
#include "rootComponent.h"

using namespace vslib;

void your_function(RootComponent& root)
{
    AbcToAlphaBetaTransform  abc_to_alphabeta("abc_to_alphabeta", root);
    // no Parameters need setting

    const double i_a  = 2.0;
    const double i_b  = -1.0;
    const double i_c  = -1.0;

    auto [alpha, beta, zero] = abc_to_alphabeta.transform(i_a, i_b, i_c);
    // alpha = 2.0, beta = 0, zero = 0
}

Example usage in a vloop:

#include "vslib.hpp"

namespace fgc::user
{
    class Converter : public vslib::RootComponent
    {
    public:
        Converter() noexcept
        : vslib::RootComponent("example"),
          interrupt_1("stg", *this, 128, vslib::InterruptPriority::high, RTTask),
          abc_to_alphabeta("abc_to_alphabeta", *this)
        {
        }

        // Define your interrupts here
        vslib::PeripheralInterrupt<Converter> interrupt_1;

        // Define your public Components here
        vslib::AbcToAlphaBetaTransform abc_to_alphabeta;

        void init() override
        {
            interrupt_1.start();
        }

        void backgroundTask() override
        {
        }

        static void RTTask(Converter& converter)
        {
            // Read the input 3-phase voltage values:
            const double v_a    = converter.m_data[0];
            const double v_b    = converter.m_data[1];
            const double v_c    = converter.m_data[2];

            // no offset, q and a alignment:
            auto [alpha, beta, zero] = abc_to_alphabeta.transform(v_a, v_b, v_c);
        }

        private:
            // actual source of data omitted for simplicity
            std::array<double, 3> m_data{0.0};
    };
}   // namespace fgc::user

alpha-beta to abc transformation

AbcToAlphaBetaTransform implements the \(\alpha\beta0\) to abc (inverse Clarke) transformation from the rotating \(\alpha\beta0\) frame to the three-phase components in the time domain. The algorithm follows the implementation and nomeclature of the Inverse Clarke Matlab implementation.

The transform method takes three obligatory double-type arguments, one for each \(\alpha\), \(\beta\), and zero components in the rotating \(\alpha\beta0\) frame of reference. The method returns a tuple of a, b, and c values. The calculation is as follows:

\[\begin{split}a &= \left( \alpha + zero \right) \\ b &= -\frac{1}{2} \alpha + \frac{\sqrt{3}}{2} \beta + zero \\ c &= -\frac{1}{2} \alpha - \frac{\sqrt{3}}{2} \beta + zero\end{split}\]

For more details regarding the API, see the API documentation for AlphaBetaToAbcTransform.

Usage example

#include "alphaBetaToAbcTransform.h"
#include "rootComponent.h"

using namespace vslib;

void your_function(RootComponent& root)
{
    AlphaBetaToAbcTransform  transform("alphabeta_to_abc");
    // no Parameters need setting

    const double alpha  = 2.0;
    const double beta  = 0.0;
    const double zero  = 0.0;

    auto [a, b, c] = transform.transform(alpha, beta, zero);
    // a = 2.0, b = -1.0, c = -1.0
}

Alpha-beta to dq0

alphaBetaToDq0Transform implements the \(\alpha\beta0\) transformation from three stationary components in the \(\alpha\beta0\) reference frame to the rotating dq0 reference frame, an equivalent of the inverse Clarke and then Park transform.

The transform method takes four obligatory double-type arguments and one optional boolean argument: one for each \(\alpha\), \(\beta\), and 0 component in the \(\alpha\beta0\) frame of reference, the \(theta\) angle (in radians) between q and \(alpha\), and optionally specify alignment: true for a-axis alignment or false for 90 degrees behind a-axis. The method returns a tuple of d, q, and 0 values. The algorithm follows the implementation and nomeclature of the alpha-beta to dq0 Matlab implementation. The calculation is as follows if the a-axis alignment is chosen:

\[\begin{split}d &= \alpha \cdot cos(\theta) + \beta \cdot sin(\theta) \\ q &= -\alpha \cdot sin(\theta) + \beta \cdot cos(\theta) \\ zero &= zero\end{split}\]

and if the the 90-degrees behind a-axis alignment is preferred:

\[\begin{split}d &= \alpha \cdot sin(\theta) - \beta \cdot cos(\theta) \\ q &= \alpha \cdot cos(\theta) + \beta \cdot sin(\theta) \\ zero &= zero\end{split}\]

For more details regarding the API, see the API documentation for AlphaBetaToDq0Transform.

Usage example

#include <numbers>

#include "alphaBetaToDq0Transform.h"
#include "rootComponent.h"

using namespace vslib;

void your_function(RootComponent& root)
{
    AlphaBetaToDq0Transform transform("alpha-beta_to_dq0", root);

    const double i_alpha     = 1.0;
    const double i_beta      = -0.5;
    const double i_zero      = 0.0;
    const double theta       = std::numbers::pi / 6;   // 30 degrees in radians
    bool   a_alignment       = true;
    auto [d, q, zero]        = transform.transform(i_alpha, i_beta, i_zero, theta, a_alignment);
}

dq0 to alpha-beta

Dq0ToAlphaBetaTransform implements the transformation of components in dq0 frame of reference to the \(\alpha\beta0\) reference frame, an inverse of AlphaBetaToDq0Transformation.

The transform method takes four obligatory double-type arguments and one optional boolean argument: one for each d, q, zero, and theta, and optionally a boolean alignment argument: true for a-axis alignment or false for 90 degrees behind a-axis. \(theta\) is the angle (in radians) between q and \(alpha\). The method returns a tuple of \(\alpha\), \(\beta\), and zero values. The algorithm follows the implementation and nomeclature of the alpha-beta to dq0 Matlab implementation (inverse).

The calculation is as follows if the a-axis alignment is chosen:

\[\begin{split}\alpha &= d \cdot cos(\theta) - q \cdot sin(\theta) \\ \beta &= d \cdot sin(\theta) + q \cdot cos(\theta) \\ zero &= i_zero\end{split}\]

and if the the 90-degrees behind a-axis alignment is preferred:

\[\begin{split}\alpha &= d \cdot sin(\theta) + q \cdot cos(\theta) \\ \beta &= -d \cdot cos(\theta) + q \cdot sin(\theta) \\ zero &= i_zero\end{split}\]

For more details regarding the API, see the API documentation for Dq0ToAlphaBetaTransform.

Usage example

#include <numbers>

#include "dq0ToAlphaBetaTransform.h"
#include "rootComponent.h"

using namespace vslib;

void your_function(RootComponent& root)
{
    dq0ToAlphaBetaTransform transform("dq0_to_alphabeta", root);

    const double d           = 1.0;
    const double q           = -0.5;
    const double i_zero      = 0.0;
    const double theta       = std::numbers::pi / 6;   // 30 degrees in radians
    bool   a_alignment       = true;
    auto [alpha, beta, zero] = transform.transform(d, q, i_zero, theta, a_alignment);
}

Performance

The execution time of each Component depends on a number of factors. In the case of AbcToAlphaBetaTransform, there no look-up tables and the execution time is independent of the inputs. For AbcToDq0Transform and AlphaBetaToDq0Transform, the execution will depend on the size of the internal look-up tables. The table below gives an overlook of the execution time that can be expected for each of the Components.

Class

Access time [ns]

AbcToAlphaBetaTransform

33

AbcToDq0Transform

263

AlphaBetaToDq0Transform

197

AlphaBetaToAbcTransform

27

Dq0ToAbcTransform

270

Dq0ToAlphaBetaTransform

230