enhanced functionality in V01 vs. V00, V02 finished, conversion to double precsision in V03

Dependencies:   mbed

GPA.cpp

Committer:
pmic
Date:
18 months ago
Revision:
22:c895fa4d7319
Parent:
21:55b11670959e

File content as of revision 22:c895fa4d7319:

/*
    GPA: frequency point wise gain and phase analyser to measure the frequency
         respone of a dynamical system

        hint:       the measurements should only be perfomed in closed loop
        assumption: the system is at the desired steady state of interest when
                    the measurment starts

                             exc(2)                 C: controller
                               |                    P: plant
                               v
     exc(1) --> o   ->| C |--->o------->| P |----------> out
                ^                  |             |
                |                   --> inp      |  exc: excitation signal (E)
                |                                |  inp: input plant (U)
                 --------------------------------   out: output plant (Y)

    instantiate option 1:
    GPA(float fMin, float fMax, int NfexcDes, int NperMin, int NmeasMin, float Ts, float Aexc0, float Aexc1)

        fMin:     minimal desired frequency that should be measured in Hz
        fMax:     maximal desired frequency that should be measured in Hz
        NfexcDes: number of logarithmic equaly spaced frequency points
        NperMin:  minimal number of periods that are used for each frequency point
        NmeasMin: minimal number of samples that are used for each frequency point
        Ts:       sampling time
        Aexc0:    excitation amplitude at fMin
        Aexc1:    excitation amplitude at fMax

        hints:    the amplitude drops with 1/fexc, if you're using
                  Axc1 = Aexc0/fMax then d/dt exc = const., this is recommended
                  if your controller does not have a rolloff.
                  if a desired frequency point is not measured try to increase Nmeas.

    pseudo code for a closed loop measurement with a controller C:

        excitation input at (1):
       
        - measuring the plant P and the closed loop tf T = PC/(1 + PC):
            desTorque = pi_w(omega_desired - omega + excWobble);
            inpWobble = desTorque;
            outWobble = omega;
            excWobble = Wobble(excWobble, outWobble);
                        
        - measuring the controller C and the closed loop tf SC = C/(1 + PC)
            desTorque = pi_w(omega_desired - omega + excWobble);
            inpWobble = n_soll + excWobble - omega;
            outWobble = desTorque;
            excWobble = Wobble(inpWobble, outWobble);

        excitation input at (2):
        
        - measuring the plant P and the closed loop tf SP = P/(1 + PC):
            desTorque = pi_w(omega_desired - omega) + excWobble;
            inpWobble = desTorque;
            outWobble = omega;
            excWobble = Wobble(excWobble, outWobble);    

    usage:
        exc(k+1) = myGPA(inp(k), out(k)) does update the internal states of the 
        gpa at the timestep k and returns the excitation signal for the timestep
        k+1. the results are plotted to a terminal (putty) over a serial 
        connection and look as follows:
 -----------------------------------------------------------------------------------------
   fexc[Hz]    |Gyu|     ang(Gyu)    |Gye|     ang(Gye)     |E|        |U|        |Y|
 -----------------------------------------------------------------------------------------
   1.000e+00  2.924e+01 -1.572e+00  1.017e+00 -4.983e-02  5.000e+00  1.739e-01  5.084e+00

    in matlab you can use:
        dataNucleo = [... insert measurement data here ...];
        g = frd(dataNucleo(:,2).*exp(1i*dataNucleo(:,3)), dataNucleo(:,1), Ts, 'Units', 'Hz');
        gcl = frd(dataNucleo(:,4).*exp(1i*dataNucleo(:,5)), dataNucleo(:,1), Ts, 'Units', 'Hz');

    if you're evaluating more than one measurement which contain equal frequency points try:
        dataNucleo = [dataNucleo1; dataNucleo2];
        [~, ind] = unique(dataNucleo(:,1),'stable');
        dataNucleo = dataNucleo(ind,:);

        autor: M.E. Peter
*/

#include "GPA.h"
#include "mbed.h"
#include "math.h"
#define   pi 3.1415927f

using namespace std;

GPA::GPA(float fMin, float fMax, int NfexcDes, int NperMin, int NmeasMin, float Ts, float Aexc0, float Aexc1)
{
    this->NfexcDes = NfexcDes;
    this->NperMin = NperMin;
    this->NmeasMin = NmeasMin;
    this->Ts = Ts;

    // calculate logarithmic spaced frequency points
    fexcDes = (float*)malloc(NfexcDes*sizeof(float));
    fexcDesLogspace(fMin, fMax, NfexcDes);

    // calculate coefficients for decreasing amplitude (1/fexc)
    this->aAexcDes = (Aexc1 - Aexc0)/(1.0f/fexcDes[NfexcDes-1] - 1.0f/fexcDes[0]);
    this->bAexcDes = Aexc0 - aAexcDes/fexcDes[0];

    fnyq = 1/2.0f/Ts;
    pi2 = 2.0f*pi;
    pi2Ts = pi2*Ts;
    piDiv2 = pi/2.0f;

    sU = (float*)malloc(3*sizeof(float));
    sY = (float*)malloc(3*sizeof(float));
    reset();
}

GPA::~GPA() {}

void GPA::reset()
{
    Nmeas = 0;
    Nper = 0;
    fexc = 0.0f;
    fexcPast = 0.0f;
    ii = 1; // iterating through desired frequency points
    jj = 1; // iterating through measurement points w.r.t. reachable frequency
    scaleG = 0.0f;
    cr = 0.0f;
    ci = 0.0f;
    for(int i = 0; i < 3; i++) {
        sU[i] = 0.0f;
        sY[i] = 0.0f;
    }
    sinarg = 0.0f;
    NmeasTotal = 0;
    Aexc = 0.0f;
    pi2Tsfexc = 0.0f;
}

float GPA::update(float inp, float out)
{
    // a new frequency point has been reached
    if(jj == 1) {
        // get a new unique frequency point
        while(fexc == fexcPast) {
            // measurement finished
            if(ii > NfexcDes) {
                return 0.0f;
            }
            calcGPAmeasPara(fexcDes[ii - 1]);
            // secure fexc is not higher or equal to nyquist frequency
            if(fexc >= fnyq) {
                fexc = fexcPast;
            }
            // no frequency found
            if(fexc == fexcPast) {
                ii += 1;
            } else {
                Aexc = aAexcDes/fexc + bAexcDes;
                pi2Tsfexc = pi2Ts*fexc;
            }
        }
        // secure sinarg starts at 0 (numerically maybe not given)
        sinarg = 0.0f;
        // filter scaling
        scaleG = 1.0f/sqrt((float)Nmeas);
        // filter coefficients
        cr = cos(pi2Tsfexc);
        ci = sin(pi2Tsfexc);
        // filter storage
        for(int i = 0; i < 3; i++) {
            sU[i] = 0.0f;
            sY[i] = 0.0f;
        }
    }
    // filter step for signal su
    sU[0] = scaleG*inp + 2.0f*cr*sU[1] - sU[2];
    sU[2] = sU[1];
    sU[1] = sU[0];
    // filter step for signal sy
    sY[0] = scaleG*out + 2.0f*cr*sY[1] - sY[2];
    sY[2] = sY[1];
    sY[1] = sY[0];
    // measurement of frequencypoint is finished
    if(jj == Nmeas) {
        jj = 1;
        ii += 1;
        fexcPast = fexc;
        // calculate the one point dft
        float Ureal = 2.0f*scaleG*(cr*sU[1] - sU[2]);
        float Uimag = 2.0f*scaleG*ci*sU[1];
        float Yreal = 2.0f*scaleG*(cr*sY[1] - sY[2]);
        float Yimag = 2.0f*scaleG*ci*sY[1];
        // calculate magnitude and angle
        float Umag = sqrt(Ureal*Ureal + Uimag*Uimag);
        float Ymag = sqrt(Yreal*Yreal + Yimag*Yimag);
        float absGyu = Ymag/Umag;
        float angGyu = atan2(Yimag, Yreal) - atan2(Uimag, Ureal);
        float absGye = Ymag/Aexc;
        float angGye = (atan2(Yimag, Yreal) + piDiv2);
        // user info
        if(ii == 2) {
            printLine();
            printf("   fexc[Hz]    |Gyu|     ang(Gyu)    |Gye|     ang(Gye)     |E|        |U|        |Y|\r\n");
            printLine();
        }
        printf("%11.3e %10.3e %10.3e %10.3e %10.3e %10.3e %10.3e %10.3e\r\n", fexc, absGyu, angGyu, absGye, angGye, Aexc, Umag, Ymag);
    } else {
        jj += 1;
    }
    sinarg = fmod(sinarg + pi2Tsfexc, pi2);
    NmeasTotal += 1;
    return Aexc*sin(sinarg);
}

void GPA::fexcDesLogspace(float fMin, float fMax, int NfexcDes)
{
    // calculate logarithmic spaced frequency points
    float Gain = log10(fMax/fMin)/((float)NfexcDes - 1.0f);
    float expon = 0.0f;
    for(int i = 0; i < NfexcDes; i++) {
        fexcDes[i] = fMin*pow(10.0f, expon);
        expon += Gain;
    }
}

void GPA::calcGPAmeasPara(float fexcDes_i)
{
    // Nmeas has to be an integer
    Nper = NperMin;
    Nmeas = (int)floor((float)Nper/fexcDes_i/Ts + 0.5f);
    //  secure that the minimal number of measurements is fullfilled
    int Ndelta = NmeasMin - Nmeas;
    if(Ndelta > 0) {
        Nper = (int)ceil((float)NmeasMin*fexcDes_i*Ts);
        Nmeas = (int)floor((float)Nper/fexcDes_i/Ts + 0.5f);
    }
    // evaluating reachable frequency
    fexc = (float)Nper/(float)Nmeas/Ts;
}

void GPA::printLine()
{
    printf("-----------------------------------------------------------------------------------------\r\n");
}

void GPA::printGPAfexcDes()
{
    printLine();
    for(int i = 0; i < NfexcDes; i++) {
        printf("%9.4f\r\n", fexcDes[i]);
    }
}

void GPA::printGPAmeasPara()
{
    printLine();
    printf(" fexcDes[Hz]  fexc[Hz]       Aexc   Nmeas   Nper\r\n");
    printLine();
    for(int i = 0; i < NfexcDes; i++) {
        calcGPAmeasPara(fexcDes[i]);
        if(fexc == fexcPast || fexc >= fnyq) {
            fexc = 0.0f;
            Nmeas = 0;
            Nper = 0;
            Aexc = 0;
        } else {
            Aexc = aAexcDes/fexc + bAexcDes;
            fexcPast = fexc;
        }
        NmeasTotal += Nmeas;
        printf("%12.2e %9.2e %10.2e %7i %6i \r\n", fexcDes[i], fexc, Aexc, Nmeas, Nper);
    }
    printGPAmeasTime();
    reset();
}

void GPA::printGPAmeasTime()
{
    printLine();
    printf(" number of data points:  %9i\r\n", NmeasTotal);
    printf(" measurment time in sec: %9.2f\r\n", (float)NmeasTotal*Ts);
}