# Lav yderligere plugin

&kst; har en enkel og standardiseret grænseflade som tillader nemt at lave oprette yderligere plugin. Udover at give detaljer om grundlæggende krav for plugin, beskriver følgende afsnit hvordan færdigskrevne deklarationsfiler kan bruges til at lave visse slags plugin.

## Lav et basalt plugin

Et &kst;-plugin består af to filer: en KDE-desktoptjenestefil og en delt objektfil.

### KDE-desktoptjenestefilen

KDE-desktoptjenestefilen sørger for information om pluginnet for infrastrukturen i &kst; som indlæser plugin. Det følgende er et eksempel på en KDE-desktoptjenestefil for et &kst;-plugin:

    [Desktop Entry]
    Encoding=UTF-8
    Type=Service
    ServiceTypes=Kst Data Object
    X-KDE-ModuleType=Plugin
    X-KDE-Library=kst_EKSEMPEL_plugin
    X-Kst-Plugin-Author=EKSEMPEL
    Name=EKSEMPEL-plugin
    Comment=EKSEMPEL algoritme for Kst.

I almindelighed kan du bruge eksemplet ovenfor som en skabelon, og ændre EKSEMPEL så det passer til dit plugin. Som det fremgår af eksemplet, består desktoptjenestefilen af en følge af nøgleværdi par. Indgangen `ServiceTypes` skal efterlades som den ser ud ovenfor. Denne nøgle angiver at pluginnet arver klassen KstDataObject. Se dokumentationen af programmeringsgrænsefladen for at finde grænsefladen som klassen eksponerer. Nøglen `X-KDE-Library` peger på den delte objektfil som implementerer dit plugin. Inkludér ikke den delte objektfils filendelse.

Når du har gjort desktopfilen færdig, gemmes den som `[X-KDE-LIBRARY].desktop`, hvor `[X-KDE-LIBRARY]` er værdien på nøglen i desktopfilen.

### Den delte objektfil

Den delte objektfil indeholder pluginnets egentlige funktion. Følgende er kravene for den delte objektfil:

- Objektet skal arve klassen KstDataObject:

      #ifndef EKSEMPELPLUGIN_H
      #define EKSEMPELPLUGIN_H

      #include <ksddataobject.h>

      class EksempelPlugin : public KstDataObject {
        Q_OBJECT
      public:
          EksempelPlugin(QObject *parent, const char *name, const QStringList &args);
          virtual ~EksempelPlugin();

          virtual KstObject::UpdateType update(int);
          virtual QString propertyString() const;
          virtual KstDataObjectPtr makeDuplicate(KstDataObjectDataObjectMap&);

      protected slots:
          virtual void _showDialog();
      };

      #endif

Følgende er et eksempel på den delte objektfils kildekode for et enkelt plugin:

### Kompilér pluginnet

Hvis du bruger &gcc; til at kompilere pluginnet, kompileres objektfilen blot:

    cc -Wall -c -o myplugin.o myplugin.c -fPIC -DPIC

og derefter oprettes det delte bibliotek:

    ld -o myplugin.so -shared myplugin.o

De resulterende filer `*.so` og `*.xml` skal placeres i samme mappe. Når du bruger plugin-håndteringen i &kst; til at indlæse XML-filen, kigger den automatisk efter den delte objektfil i samme mappe.

## Lav et plugin for lineærindstilling

For at lave et plugin for lineærindstilling, kan du implementere dine egne indstillingsalgoritmer og give passende vektorer som uddata. &kst; levereres dog allerede med deklarationsfiler som gør det enkelt for dig at implementere plugin for lineær mindste kvadratindstilling ved kun at sørge for nogle få funktioner. Dette afsnit beskriver hvordan man får nytte af disse filer.

### Deklarationsfiler

Der sørges for to deklarationsfiler til at udføre lineærindstillinger, `linear.h` (for uvægtede lineærindstillinger) og `linear_weighted.h` (for vægtede lineærindstillinger). Begge findes under `kst(plugins/fits/` i kildekodearkivet for &kst;. For at bruge filerne, inkluderes blot en af dem i dit plugins kildekode:

    #include <../linear.h>

eller

    #include <../linear_weighted.h>

(pr konvention placerer vi kildekoden for pluginnet et mappeniveau under stedet hvor deklarationsfilerne er).

### Implementér nødvendige funktioner

Givet en generel lineær model:

hvor `y` er en vektor med `n` observationer, `X` er en matrix med størrelsen `n` gange `p` med prediktorvariabler, og `c` er vektoren med `p` parametre for bedste indstilling som skal estimeres. Deklarationsfilen sørger for funktioner til at estimere `c` for et givet `y` og `X`. For at sørge for `X`, skal følgende funktion implementeres i pluginnets kildekode:

    double calculate_matrix_entry( double dX, int iPos )

Funktionen skal returnere værdien af resultatet i søjle `iPos` fra matricen med prediktorvariabler, ved at bruge `x`-værdien `dX`. Funktionen kaldes af linear.h eller linear_weighted.h. Implementeringen af funktionen afhænger af modellen du vil bruge til indstillingen, og er entydig for hvert plugin for lineærindstilling. For eksempel for at tilpasse en polynommodel, vil funktionen `calculate_matrix_entry` kunne implementeres på følgende måde:

    double calculate_matrix_entry( double dX, int iPos ) {
      double dY;
      dY = pow( dX, (double)iPos );
      return dY;
    }

### Kalde tilpasningfunktionerne

Når en passende deklarationsfil er inkluderet og funktionen `calculate_matrix_entry` er implementeret, kaldes en passende indstillingsfunktion som inkluderes fra deklarationsfilen:

    kstfit_linear_unweighted( inArrays, inArrayLens,
                              outArrays, outArrayLens,
                              outScalars, iNumParams );

eller

    kstfit_linear_weighted( inArrays, inArrayLens,
                            outArrays, outArrayLens,
                            outScalars, iNumParams );

Hver funktion returnerer `0` når det lykkes, eller `-1` ved fejl, så det er en god idé at lade returværdien for den eksporterede C-funktion være den samme som returværdien for indstillingsfunktionen. For at holde det enkelt, kan koden for pluginnet blot sende argumenterne videre som gives til den eksporterede C-funktion til tilpasningsfunktionen. Bemærk dog at inArrays skal have følgende struktur:

- `inArrays[0]` skal indeholde feltet med x-koordinater for datapunkterne

- `inArrays[1]` skal indeholde feltet med y-koordinater for datapunkterne

- `inArrays[2]` er der kun hvis `kstfit_linear_weighted` kaldes, og skal indeholde feltet med vægtninger for tilpasningen.

Den nemmeste måde at sikre at inArrays er rigtigt struktureret er at angive de n rigtige rækkefølge for inddatavektorer i pluginnets XML-fil.

`iNumParams` er antal parametre som indgår i den brugte tilpasningsmodel, hvilket skal være lig med antal søjler i matricen `X` med prediktorvariabler. `iNumParams` skal angives rigtigt før tilpasningsfunktionen kaldes.

Efter funktionen `kstfit_linear_unweighted` eller `kstfit_linear_weighted` er kaldet, er `outArrays` og `outScalars` blevet tildelt følgende:

- `outArrays[0]` vil indeholde feltet med tilpassede y-værdier.

- `outArrays[1]` vil indeholde feltet med rester.

- `outArrays[2]` vil indeholde feltet med estimerede parametre for bedste tilpasning.

- `outArrays[3]` vil indeholde kovariansmatricen, returneret linje for linje i feltet.

- `outScalars[0]` vil indeholde chi^2/nu, hvor chi^2 er den vægtede sum af kvadratresternes, og nu frihedsgraderne.

`outArrayLens` vil have den rigtige værdi til at angive længden for hvert uddatafelt.

Sørg for at angiven uddata i XML-filen passer sammen med dem som de eksporterede C-funktioner returnerer (hvilket i de fleste tilfælde simpelthen er uddata fra tilpasningsfunktionen).

### Eksempel

Det følgende er et eksempel på kildekoden for et plugin for lineærindstilling.

    /*
     *  Polynomial fitting plugin for KST.
     *  Copyright 2004, The University of British Columbia
     *  Released under the terms of the GPL.
     */

    #include "../linear.h"

    double calculate_matrix_entry( double dX, int iPos ) {
      double dY;

      dY = pow( dX, (double)iPos );

      return dY;
    }

    extern "C" int kstfit_polynomial_unweighted(
      const double *const inArrays[],
      const int inArrayLens[],
      const double inScalars[],
      double *outArrays[], int outArrayLens[],
      double outScalars[]);

    int kstfit_polynomial_unweighted(
      const double *const inArrays[],
      const int inArrayLens[],
            const double inScalars[],
            double *outArrays[], int outArrayLens[],
            double outScalars[])
    {
      int iRetVal = -1;
      int iNumParams;

      iNumParams = 1 + (int)floor( inScalars[0] );
      if( iNumParams > 0 ) {
        iRetVal = kstfit_linear_unweighted( inArrays, inArrayLens,
                                            outArrays, outArrayLens,
                                            outScalars, iNumParams );
      }

      return iRetVal;
    }

## Lav plugin for ikke-lineær indstilling

&kst; sørger for deklarationsfiler for at forenkle implementeringen af plugin med ikke-lineære mindste kvadrat tilpasning. Brugen af deklarationsfilerne beskrives nedenfor.

### Deklarationsfiler og definitioner

Deklarationsfilerne for ikke-lineær indstilling er under `kst(plugins/fits_nonlinear/` i kildekodearkivet for &kst;. Filerne hedder `non_linear.h` og `non_linear_weighted.h` for henholdsvis uvægtede og vægtede tilpasninger. For at bruge filerne, inkluderes kun en af dem i dit plugins kildekode:

    #include <../non_linear.h>

eller

    #include <../non_linear_weighted.h>

(pr konvention placerer vi kildekoden for pluginnet et mappeniveau under stedet hvor deklarationsfilerne er).

Eftersom ikke-lineær tilpasning er en iterativ proces, skal du også definere et maksimalt antal iterationer som skal udføres. Algoritmen for ikke-lineær indstilling stopper når mindst en af følgende betingelser er sand:

- Maksimalt antal iterationer er opnået.

- Præcisionen 10-4 er opnået.

Desuden skal du definere antal parametre i modellen, eftersom de ikke eksplicit sendes til tilpasningsfunktionen. For at definere disse to værdier, inkluderes følgende længst oppe i din kildekode:

    #define NUM_PARAMS [num1]
    #define MAX_NUM_ITERATIONS [num2]

som erstatter `[num1]` med antal parametre i modellen, og `[num2]` med maksimalt antal iterationer at udføre.

### Implementér nødvendige funktioner

For at bruge deklarationsfilen for ikke-lineær tilpasning, skal du sørge for funktionen som skal bruges som model, partielle afledede for funktionen med hensyn til hver parameter og de oprindelige estimater for parametrene for bedste tilpasning. For at gøre dette skal tre funktioner implementeres. Disse funktioner beskrives nedenfor.

`double function_calculate( double `dX`, double* `pdParameters` )`

  Denne funktion beregner Y-værdien for tilpasningsmodellen for en given X-værdi `dX`, med det felt der sørges for med parametre `pdParameters`. Parametrenes rækkefølge i `pdParameters` er vilkårlig, men skal stemme overens med de to andre implementerede funktioner. For eksempel for en eksponentiel model, vil `function_calculate` kunne implementeres på følgende måde:

      double function_calculate( double dX, double* pdParameters ) {
        double dScale         = pdParameters[0];
        double dLambda = pdParameters[1];
        double dOffset = pdParameters[2];
        double dY;

        dY  = ( dScale * exp( -dLambda * dX ) ) + dOffset;

        return dY;
      }

`void function_derivative( double `dX`, double* `pdParameters`, double* `pdDerivatives` )`

  Denne funktion beregner partielle afledede for modelfunktionen for en givet X-værdi `dX`. De partielle afledede skal returneres i `pdDerivatives`. Rækkefølgen af de partielle afledede i feltet `pdDerivatives` skal svare til rækkefølgen af parametrene i `pdParameters` (dvs. hvis `pdParameters[0]` indeholder parameteren lambda for en eksponentialmodel, skal `pdDerivatives[0]` indeholde den afledede for modellen med hensyn til lambda).

`void function_initial_estimate( const double* `pdX`, const double* `pdY`, int `iLength`, double* `pdParameterEstimates` )`

  Denne funktion sørger for et oprindelig estimat af parametrene for bedste tilpasning for tilpasningsfunktionen. Feltet med X- og Y-værdier for datapunkterne er henholdsvis i `pdX` og `pdY`, og antal datapunkter angives af `iLength`. Du kan bruge alle eller ingen af parametrene efter behag til at beregne det oprindelige estimat. Funktionen skal placere de beregnede oprindelige estimater i `pdParameterEstimates`, og lade estimaternes rækkefølge stemme med rækkefølgen for parametrene i `pdParameters` i `function_calculate` og `function_derivative`. Husk at det oprindelige estimat er vigtigt for at afgøre om tilpasningsfunktionen konvergerer mod en løsning.

### Kalde tilpasningfunktionerne

Når alle nødvendige funktioner er implementeret, kan tilpasningsfunktionen fra den inkluderede deklarationsfil kaldes:

    kstfit_nonlinear( inArrays, inArrayLens,
                      inScalars, outArrays,
                      outArrayLens, outScalars );

eller

    kstfit_nonlinear_weighted( inArrays, inArrayLens,
                               inScalars, outArrays,
                               outArrayLens, outScalars );

afhængig af om du implementerer en uvægtet tilpasning eller en vægtet tilpasning.

Funktionen returnerer `0` når det lykkes, eller `-1` ved en fejl, så det er nemmest at lade returværdien for den eksporterede C-funktion være den samme som returværdien for tilpasningsfunktionen. For at holde det enkelt, kan koden for pluginnet blot sende argumenterne som gives videre til den eksporterede C-funktion til tilpasningsfunktionen. Bemærk dog at inArrays skal have følgende struktur:

- `inArrays[0]` skal indeholde feltet med x-koordinater for datapunkterne

- `inArrays[1]` skal indeholde feltet med y-koordinater for datapunkterne

- `inArrays[2]` er der kun hvis `kstfit_linear_weighted` kaldes, og skal indeholde feltet med vægtninger for tilpasningen.

Den nemmeste måde at sikre at inArrays er rigtigt struktureret er at angive de n rigtige rækkefølge for inddatavektorer i pluginnets XML-fil.

Efter funktionen `kstfit_linear_unweighted` eller `kstfit_linear_weighted` er kaldet, er `outArrays` og `outScalars` blevet tildelt følgende:

- `outArrays[0]` vil indeholde feltet med tilpassede y-værdier.

- `outArrays[1]` vil indeholde feltet med rester.

- `outArrays[2]` vil indeholde feltet med estimerede parametre for bedste tilpasning.

- `outArrays[3]` vil indeholde kovariansmatricen, returneret linje for linje i feltet.

- `outScalars[0]` vil indeholde chi^2/nu, hvor chi^2 er den vægtede sum af kvadratresternes, og nu frihedsgraderne.

`outArrayLens` vil have den rigtige værdi til at angive længden for hvert uddatafelt.

Sørg for at angiven uddata i XML-filen passer sammen med dem som de eksporterede C-funktioner returnerer (hvilket i de fleste tilfælde simpelthen er uddata fra tilpasningsfunktionen).

### Eksempel

Det følgende er et eksempel på et ikke-lineært tilpasningsplugin som udfører en tilpasning til en eksponentiel model.

    /*
     *  Exponential fit plugin for KST.
     *  Copyright 2004, The University of British Columbia
     *  Released under the terms of the GPL.
     */

    #define NUM_PARAMS 3
    #define MAX_NUM_ITERATIONS 500

    #include "../non_linear.h"

    void function_initial_estimate( const double* pdX, const double* pdY,
                                    int iLength, double* pdParameterEstimates ) {
      KST_UNUSED( pdX )
      KST_UNUSED( pdY )
      KST_UNUSED( iLength )

      pdParameterEstimates[0] =  1.0;
      pdParameterEstimates[1] =  0.0;
      pdParameterEstimates[2] =  0.0;
    }

    double function_calculate( double dX, double* pdParameters ) {
      double dScale         = pdParameters[0];
      double dLambda = pdParameters[1];
      double dOffset = pdParameters[2];
      double dY;

      dY  = ( dScale * exp( -dLambda * dX ) ) + dOffset;

      return dY;
    }

    void function_derivative( double dX, double* pdParameters, double* pdDerivatives ) {
      double dScale         = pdParameters[0];
      double dLambda = pdParameters[1];
      double dExp;
      double ddScale;
      double ddLambda;
      double ddOffset;

      dExp     = exp( -dLambda * dX );
      ddScale  = dExp;
      ddLambda = -dX * dScale * dExp;
      ddOffset = 1.0;

      pdDerivatives[0] = ddScale;
      pdDerivatives[1] = ddLambda;
      pdDerivatives[2] = ddOffset;
    }

    extern "C" int kstfit_eksponential(const double *const inArrays[], const int inArrayLens[],
                    const double inScalars[],
                    double *outArrays[], int outArrayLens[],
                    double outScalars[]);

    int kstfit_eksponential(const double *const inArrays[], const int inArrayLens[],
                    const double inScalars[],
                    double *outArrays[], int outArrayLens[],
                    double outScalars[])
    {
      return kstfit_nonlinear( inArrays, inArrayLens,
                               inScalars, outArrays,
                               outArrayLens, outScalars );
    }

## Lav plugin for passfilter

&kst; sørger for deklarationsfiler for at forenkle implementeringen af plugin med passfilter. Brugen af deklarationsfilerne beskrives nedenfor.

### Deklarationsfiler

Deklarationsfilen for passfiltret er i `kst/plugins/pass_filters` i kildekodearkivet for &kst;. Filen hedder `filters.h`. For at bruge filen, inkluderes den i dit plugins kildekode:

    #include <../filters.h>

(pr konvention placerer vi kildekoden for pluginnet et mappeniveau under stedet hvor deklarationsfilerne er).

### Nødvendige funktioner

Deklarationsfilen `filters.h` indeholder en enkelt funktion som beregner Fouriertransformationen for en funktion der sørges for, anvender det filter der er sørget for på Fouriertransformationen og derefter beregner den inverse Fouriertransformation for den filtrerede transformen. For at sørge for filtret, skal følgende funktion implementeres i dit plugins kildekode:

`double filter_calculate( double `dFreqValue`, const double `inScalars[]` )`

Funktionen skal beregne filtreret amplitude for frekvensen `dFreqValue`. `inScalars[]` indeholder de uændrede inddataskalarer for pluginnet, som angives i XML-filen. Sandsynligvis indeholder `inScalars[]` afskæringsfrekvenser eller andre egenskaber for filtret. For eksempel for at implementere et Butterworth højpassfilter, kan funktionen `filter_calculate` implementeres som følger:

    double filter_calculate( double dFreqValue, const double inScalars[] ) {
      double dValue;
      if( dFreqValue > 0.0 ) {
        dValue = 1.0 / ( 1.0 +
                        pow( inScalars[1] / dFreqValue, 2.0 * (double)inScalars[0] ) );
      } else {
        dValue = 0.0;
      }
      return dValue;
    }

### Kald filterfunktionen

Så snart den nødvendige `filter_calculate` er implementeret, kan filterfunktionen fra deklarationsfilen kaldes:

    kst_pass_filter( inArrays,
                     inArrayLens,
                     inScalars,
                     outArrays,
                     outArrayLens,
                     outScalars );

Argumenterne som sendes til den eksporterede C-funktion kan sædvanligvis sendes til `kst_pass_filter` uden ændringer. Der er dog nogle begrænsninger for argumenterne:

- `inArrays[0]` skal indeholde feltet med data der skal filtreres.

- `inScalars` skal indeholde filterspecifikke parametre som skal bruges af funktionen `filter_calculate`.

Efter funktionskaldet kommer `outArrays[0]` til at indeholde det filtrerede datafelt, og `outArrayLens` vil blive sat passende. Funktionen `kst_pass_filter` bruger ikke `outScalars`.

### Eksempel

Det følgende er et eksempel på et plugin med et passfilter som implementerer Butterworth højpassfiltrering.

    /*
     *  Butterworth low pass filter plugin for KST.
     *  Copyright 2004, The University of British Columbia
     *  Released under the terms of the GPL.
     */

    #include <stdlib.h>
    #include <math.h>
    #include "../filters.h"

    extern "C" int butterworth_highpass(const double *const inArrays[], const int inArrayLens[],
                    const double inScalars[],
                    double *outArrays[], int outArrayLens[],
                    double outScalars[]);

    int butterworth_highpass(const double *const inArrays[], const int inArrayLens[],
                    const double inScalars[],
                    double *outArrays[], int outArrayLens[],
                    double outScalars[])
    {
      int iReturn;

      iReturn = kst_pass_filter( inArrays,
                                 inArrayLens,
                                 inScalars,
                                 outArrays,
                                 outArrayLens,
                                 outScalars );

      return iReturn;
    }

    double filter_calculate( double dFreqValue, const double inScalars[] ) {
      double dValue;

      if( dFreqValue > 0.0 ) {
        dValue = 1.0 / ( 1.0 + pow( inScalars[1] / dFreqValue, 2.0 * (double)inScalars[0] ) );
      } else {
        dValue = 0.0;
      }

      return dValue;
    }