Search code examples
c++functioncontrolssystemtransfer

How do I implement control system block of a transfer function in C++?


I want to define a control system block like:

class ControlSystemBlock
{
 public:
  ControlSystemBlock()
  {
   m_dbTimeStep = 0.001; // time between two consequential inputs
  }

  // this method can be called anytime,
  // but the object will assume
  // that it was called
  // after m_dbTimeStep before the last call
  void LoadNewInput(double dbInputValue);

  double GetCurrentOutput();
  // ...
 private:
  double m_dbTimeStep;
  // ...
};

The system will receive inputs, and according to these inputs and user defined transfer-function in it, its output value will change in time.

For instance, assume that I want to implement the transfer function H(s) = 1 / (s + 2). How do I do it? Is there any algorithm for that?


Solution

  • What do you think of my code:

    ControlSystemBlock.h

    #ifndef CONTROLSYSTEMBLOCK_H
    #define CONTROLSYSTEMBLOCK_H
    
    #include <vector>
    #include <deque>
    #include "Polynomial.h"
    #include "PolynomialFraction.h"
    
    class ControlSystemBlock
    {
        public:
            enum SIGNAL_TYPE
            {
                ST_DISCRETE     = 1,
                ST_CONTINUOUS   = 2
            };
    
            ControlSystemBlock( long double dbSamplingPeriod = 0.001);
            ControlSystemBlock( const std::vector<long double> & NominatorCoefficients,
                                const std::vector<long double> & DenominatorCoefficients,
                                SIGNAL_TYPE SignalType = SIGNAL_TYPE::ST_CONTINUOUS,
                                long double dbSamplingPeriod = 0.001);
            ControlSystemBlock( const Polynomial<long double> & NominatorPolynomial,
                                const Polynomial<long double> & DenominatorPolynomial,
                                SIGNAL_TYPE SignalType = SIGNAL_TYPE::ST_CONTINUOUS,
                                long double dbSamplingPeriod = 0.001);
            ControlSystemBlock( const PolynomialFraction<long double> & TransferFunction,
                                SIGNAL_TYPE SignalType = SIGNAL_TYPE::ST_CONTINUOUS,
                                long double dbSamplingPeriod = 0.001);
    
            // Sends a new input to the system block
            // Assuming that this input is sent just after m_dbSamplingPeriod seconds after the last input
            // Returns the the new output value
            long double SendInput(long double dbInput);
            long double GetOutput() const;
    
        protected:
            long double m_dbSamplingPeriod;
            std::deque<long double> m_InputMemory;
            std::deque<long double> m_OutputMemory;
            void SetTransferFunction(const PolynomialFraction<long double> & TransferFunction, SIGNAL_TYPE SignalType);
            PolynomialFraction<long double> m_TransferFunction;
    
        private:
            PolynomialFraction<long double> ContinuousTimeToDiscreteTime(const PolynomialFraction<long double> & ContinuousTimeTransferFunction);
            PolynomialFraction<long double> ContinuousTimeToDiscreteTime(const Polynomial<long double> & NominatorPolynomial,
                                                                         const Polynomial<long double> & DenominatorPolynomial);
            PolynomialFraction<long double> ContinuousTimeToDiscreteTime(const std::vector<long double> & NominatorCoefficients,
                                                                         const std::vector<long double> & DenominatorCoefficients);
            void ShiftMemoryRegisters(long double dbNewInput);
    };
    
    #endif
    

    ControlSystemBlock.cpp

    #include "ControlSystemBlock.h"
    
    ControlSystemBlock::ControlSystemBlock( long double dbSamplingPeriod /*= 0.001*/)
    {
        m_dbSamplingPeriod = dbSamplingPeriod;
        std::vector<long double> Coefficients;
        Coefficients.push_back(1.0);
        PolynomialFraction<long double> TransferFunction(Coefficients, Coefficients);
        SetTransferFunction(TransferFunction, SIGNAL_TYPE::ST_DISCRETE);
    }
    
    ControlSystemBlock::ControlSystemBlock( const std::vector<long double> & NominatorCoefficients,
                                            const std::vector<long double> & DenominatorCoefficients,
                                            SIGNAL_TYPE SignalType /*= SIGNAL_TYPE::ST_CONTINUOUS*/,
                                            long double dbSamplingPeriod /*= 0.001*/)
    {
        m_dbSamplingPeriod = dbSamplingPeriod;
        PolynomialFraction<long double> TransferFunction = PolynomialFraction<long double>(NominatorCoefficients, DenominatorCoefficients);
        SetTransferFunction(TransferFunction, SignalType);
    }
    
    ControlSystemBlock::ControlSystemBlock( const Polynomial<long double> & NominatorPolynomial,
                                            const Polynomial<long double> & DenominatorPolynomial,
                                            SIGNAL_TYPE SignalType /*= SIGNAL_TYPE::ST_CONTINUOUS*/,
                                            long double dbSamplingPeriod /*= 0.001*/ )
    {
        m_dbSamplingPeriod = dbSamplingPeriod;
        PolynomialFraction<long double> TransferFunction = PolynomialFraction<long double>(NominatorPolynomial, DenominatorPolynomial);
        SetTransferFunction(TransferFunction, SignalType);
    }
    
    ControlSystemBlock::ControlSystemBlock( const PolynomialFraction<long double> & TransferFunction,
                                            ControlSystemBlock::SIGNAL_TYPE SignalType /*= SIGNAL_TYPE::ST_CONTINUOUS*/,
                                            long double dbSamplingPeriod /*= 0.001*/)
    {
        m_dbSamplingPeriod = dbSamplingPeriod;
        if (SignalType == SIGNAL_TYPE::ST_CONTINUOUS)
        SetTransferFunction(TransferFunction, SignalType);
    }
    
    long double ControlSystemBlock::SendInput(long double dbInput)
    {
        ShiftMemoryRegisters(dbInput);
        long double dbSumX = 0.0, dbSumY = 0.0;
        for (uint64_t i=0; i<m_TransferFunction.GetNominatorDegree()+1; i++)
        {
            dbSumX += m_TransferFunction.GetNominator().GetCoefficientAt(i) * m_InputMemory.at(i);
        }
        for (uint64_t i=1; i<m_TransferFunction.GetDenominatorDegree()+1; i++)
        {
            dbSumY += m_TransferFunction.GetDenominator().GetCoefficientAt(i) * m_OutputMemory.at(i);
        }
        return m_OutputMemory.at(0) = (dbSumX - dbSumY) / m_TransferFunction.GetDenominator().GetCoefficientAt(0);
    }
    
    long double ControlSystemBlock::GetOutput() const
    {
        return m_OutputMemory.at(0);
    }
    
    PolynomialFraction<long double> ControlSystemBlock::ContinuousTimeToDiscreteTime(const PolynomialFraction<long double> & ContinuousTimeTransferFunction)
    {
        // Generate an "s" term in terms of "z^(-1)" terms
        std::vector<long double> nom, den;
        nom.push_back(1);
        nom.push_back(-1);
        den.push_back(1);
        den.push_back(1);
        PolynomialFraction<long double> STerm(nom, den);
        STerm *= static_cast<long double>(2) / m_dbSamplingPeriod;
        // Define nominator and denominator terms of the discrete time transfer function separately
        nom.clear();
        den.clear();
        nom.push_back(0);
        nom.push_back(1);
        PolynomialFraction<long double> NominatorOfDiscreteTimeTransferFunction(nom, den);
        PolynomialFraction<long double> DenominatorOfDiscreteTimeTransferFunction(nom, den);
        // Generate the nominator and denominator terms of the resulting discrete time transfer function
        for (uint64_t i=0; i<ContinuousTimeTransferFunction.GetNominatorDegree()+1; i++)
        {
            NominatorOfDiscreteTimeTransferFunction += STerm.GetPower(i) * ContinuousTimeTransferFunction.GetNominator().GetCoefficientAt(i);
        }
        for (uint64_t i=0; i<ContinuousTimeTransferFunction.GetDenominatorDegree()+1; i++)
        {
            NominatorOfDiscreteTimeTransferFunction += STerm.GetPower(i) * ContinuousTimeTransferFunction.GetDenominator().GetCoefficientAt(i);
        }
        return NominatorOfDiscreteTimeTransferFunction / DenominatorOfDiscreteTimeTransferFunction;
    }
    
    PolynomialFraction<long double> ControlSystemBlock::ContinuousTimeToDiscreteTime(const Polynomial<long double> & NominatorPolynomial,
                                                                                     const Polynomial<long double> & DenominatorPolynomial)
    {
        PolynomialFraction<long double> ContinuousTimeTransferFunction(NominatorPolynomial, DenominatorPolynomial);
        return ContinuousTimeToDiscreteTime(ContinuousTimeTransferFunction);
    }
    
    PolynomialFraction<long double> ControlSystemBlock::ContinuousTimeToDiscreteTime(const std::vector<long double> & NominatorCoefficients,
                                                                                     const std::vector<long double> & DenominatorCoefficients)
    {
        PolynomialFraction<long double> ContinuousTimeTransferFunction(NominatorCoefficients, DenominatorCoefficients);
        return ContinuousTimeToDiscreteTime(ContinuousTimeTransferFunction);
    }
    
    void ControlSystemBlock::SetTransferFunction( const PolynomialFraction<long double> & TransferFunction, SIGNAL_TYPE SignalType)
    {
        if (SignalType == SIGNAL_TYPE::ST_CONTINUOUS)
        {
            m_TransferFunction = ContinuousTimeToDiscreteTime(TransferFunction);
        }
        else
        {
            m_TransferFunction = TransferFunction;
        }
        m_InputMemory.resize(m_TransferFunction.GetNominatorDegree() + 1, 0.0);
        m_OutputMemory.resize(m_TransferFunction.GetDenominatorDegree() + 1, 0.0);
    }
    
    void ControlSystemBlock::ShiftMemoryRegisters(long double dbNewInput)
    {
        m_InputMemory.push_back(dbNewInput);
        m_InputMemory.pop_front();
        m_OutputMemory.push_back(0.0);
        m_OutputMemory.pop_front();
    }
    

    I have just finished it.
    I going to test it soon.