# Criar 'Plugins' Adicionais

O &kst; tem uma interface simples e normalizada que facilita a criação de 'plugins' adicionais. Para além de detalhar os requisitos básicos para os 'plugins', as secções seguintes descrevem como tirar partido dos ficheiros de inclusão pré-gravados para criar certos tipos de 'plugins'.

## Criar um 'Plugin' Básico

Um 'plugin' do &kst; consiste em dois ficheiros — um ficheiro 'desktop' de serviços do KDE e um ficheiro-objecto dinâmico.

### O Ficheiro 'Desktop' de Serviços do KDE

O ficheiro 'desktop' de serviços do KDE oferece informações sobre o 'plugin' à infra-estrutura de carregamento de 'plugins' do &kst;. Segue-se um exemplo de um ficheiro 'desktop' do KDE para um 'plugin' do &kst;:

    [Desktop Entry]
    Encoding=UTF-8
    Type=Service
    ServiceTypes=Kst Data Object
    X-KDE-ModuleType=Plugin
    X-KDE-Library=kst_XPTO_plugin
    X-Kst-Plugin-Author=XPTO
    Name=Módulo XPTO
    Comment=O algoritmo XPTO do Kst.

De um modo geral, poderá usar o exemplo acima como modelo, modificando os itens XPTO para se adequar ao seu 'plugin'. Como poderá ser visto no exemplo, o ficheiro 'desktop' do serviço consiste numa série de pares (chave, valor). O item `ServiceTypes` deverá ser deixado como está acima. Este identificador indica que o 'plugin' herda da classe KstDataObject. Veja a documentação da API para saber as interfaces que esta classe expõe. O item `X-KDE-Library` aponta para o ficheiro-objecto dinâmico que implementa o seu 'plugin'. Não inclua a extensão do ficheiro-objecto.

Logo que tenha terminado o ficheiro 'desktop', grave-o como `[nomemodulo].xml`, em que o `[nomemodulo]` é o valor da chave no ficheiro 'desktop'.

### O Ficheiro com o Objecto Partilhado

O ficheiro-objecto dinâmico contém a funcionalidade real do 'plugin'. Seguem-se os requisitos do ficheiro-objecto dinâmico:

- O objecto deve herdar da classe KstDataObject:

      #ifndef XPTOPLUGIN_H
      #define XPTOPLUGIN_H

      #include <kstdataobject.h>

      class XptoPlugin : public KstDataObject {
        Q_OBJECT
      public:
          XptoPlugin(QObject *mae, const char *nome, const QStringList &args);
          virtual ~XptoPlugin();

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

      protected slots:
          virtual void _showDialog();
      };

      #endif

O seguinte é um exemplo do ficheiro-objecto dinâmico para um 'plugin' simples:

### Compilar o 'Plugin'

Se está a usar o &gcc; para compilar o seu 'plugin', compile simplesmente o ficheiro-objecto:

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

e crie então a biblioteca dinâmica:

    ld -o omeuplugin.so -shared omeuplugin.o

Os ficheiros `*.so` e `*.xml` resultantes deverão ser colocados na mesma pasta. Quando você usar o Gestor de 'Plugins' do &kst; para carregar o ficheiro XML, ele irá ver automaticamente o ficheiro-objecto partilhado para a mesma pasta.

## Criar 'Plugins' de Ajuste Linear

Para criar um 'plugin' de ajuste linear, você poderia implementar os seus próprios algoritmos de ajuste e devolver como resultado os vectores apropriados. Contudo, o &kst; já vem com ficheiros de inclusão que lhe facilitam a implementação de 'plugins' de ajuste aos quadrados mínimos, oferecendo apenas algumas funções. Esta secção irá descrever como tirar partido destes ficheiros.

### Ficheiros de Inclusão

São oferecidos dois ficheiros de inclusão para efectuar ajustes lineares, o `linear.h` (para os ajustes lineares sem pesos) e o `linear_weighted.h` (para os ajustes lineares com pesos de ponderação). Localizam-se ambos em `kst/plugins/fits/` no pacote de código do &kst;. Para usar estes ficheiros, inclua apenas um deles no ficheiro de código do seu 'plugin':

    #include <../linear.h>

ou

    #include <../linear_weighted.h>

(por convenção, iremos colocar o código-fonte do 'plugin' uma pasta abaixo do local onde os ficheiros de inclusão se encontram).

### Implementar as Funções Necessárias

Dado um modelo linear geral:

em que o `y` é um vector com `n` observações, o `X` é uma matriz de `n` por `p` variáveis de predicção e o `c` é o vector de `p` parâmetros óptimos de ajuste que serão estimados, os ficheiros de inclusão oferecem funções para estimar o `c` para um dado `y` e `X`. Para fornecer o `X`, a seguinte função necessita ser implementada no código-fonte do 'plugin':

    double calculate_matrix_entry( double dX, int iPos )

Esta função deverá devolver o valor do elemento na coluna `iPos` da matriz de variáveis de predicção, usando o valor de `x` `dX`. Esta função será chamada pelo linear.h ou pelo linear_weighted.h. A implementação desta função depende do modelo que deseja usar para o ajuste e é única para cada 'plugin' de ajuste linear. Por exemplo, para se ajustar a um modelo polinomial, o `calculate_matrix_entry` poderá ser implementado da seguinte forma:

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

### Invocar as Funções de Ajuste

Logo que o ficheiro de inclusão apropriado tenha sido incluído e o `calculate_matrix_entry` tenha sido implementado, chame a função de ajuste apropriada que existe no ficheiro de inclusão:

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

ou

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

Cada função irá devolver `0` em caso de sucesso ou `-1` em caso de erro, por isso é uma boa ideia definir o valor devolvido pela função de C exportada como sendo igual ao valor devolvido pela função de ajuste. Para manter a simplicidade, o código do 'plugin' poderá simplesmente passar os argumentos indicados na função exportada para C para a função de ajuste. Repare, contudo, que o 'inArrays' deverá estar estruturado da seguinte forma:

- O `inArrays[0]` deverá conter a lista de coordenadas em X dos pontos de dados.

- O `inArrays[1]` deverá conter a lista de coordenadas em Y dos pontos de dados

- O `inArrays[2]` só existe se o `kstfit_linear_weighted` for invocado e deverá conter a lista de pesos a usar como ponderação para o ajuste.

A forma mais simples de garantir que o 'inArrays' está estruturado correctamente é indicar a ordem correcta dos vectores de entrada no ficheiro XML para o 'plugin'.

O `iNumParams` é o número de parâmetros no modelo de ajuste usado, o qual deverá ser igual ao número de colunas a matriz `X` de variáveis de predicção. O `iNumParams` deverá estar configurado correctamente antes de a função de ajuste ser chamada.

Depois de o `kstfit_linear_unweighted` ou o `kstfit_linear_weighted` terem sido chamados, o `outArrays` e o `outScalars` serão definidos da seguinte forma:

- O `outArrays[0]` irá conter a lista de valores ajustados em Y.

- O `outArrays[1]` irá conter a lista dos valores residuais.

- O `outArrays[2]` irá conter a lista dos parâmetros de ajuste óptimo que foram estimados.

- O `outArrays[3]` irá conter a matriz de covariância, devolvida linha após linha numa lista.

- O `outScalars[0]` irá conter o chi^2/nu, em que o chi^2 é a soma ponderada dos quadrados dos valores residuais, enquanto o nu é o número de graus de liberdade.

O `outArrayLens` será configurado correctamente para indicar o tamanho de cada lista à saída.

Garanta que os resultados indicados no ficheiro XML correspondem aos que a função em C exportada devolve (o que, na maioria dos casos, será simplesmente os valores à saída devolvidos pela função de ajuste).

### Exemplo

Segue-se um exemplo do código-fonte para um 'plugin' de ajuste linear.

    /*
     *  'Plugin' de ajuste polinomial para o KST.
     *  Copyright 2004, The University of British Columbia
     *  Lançado segundo os termos da 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;
    }

## Criar 'Plugins' de Ajuste Não-Linear

O &kst; oferece ficheiros de inclusão desenhados para simplificar a criação dos 'plugins' de ajuste dos quadrados mínimos. As secções seguintes entram em detalhe na utilização dos ficheiros de inclusão.

### Ficheiros de Inclusão e Definições

Os ficheiros de inclusão localizam-se na pasta `kst/plugins/fits_nonlinear` do pacote de código-fonte do &kst;. Os ficheiros chamam-se `non_linear.h` e `non_linear_weighted.h` para os ajustes não-ponderados e ponderados, respectivamente. Para usar estes ficheiros, inclua apenas um deles no ficheiro de código do seu 'plugin':

    #include <../non_linear.h>

ou

    #include <../non_linear_weighted.h>

(por convenção, será colocado o código-fonte para o 'plugin' uma pasta abaixo do local em que os ficheiros de inclusão se encontram).

Dado que o ajuste não-linear é um processo iterativo, você precisa de definir também o número máximo de iterações que deverão ser efectuadas. O algoritmo de ajuste não-linear irá parar quando pelo menos uma das condições for verdadeira:

- O número máximo de iterações foi atingido.

- Foi atingida uma precisão de 10-4.

Para além disso, o utilizador precisa de definir o número de parâmetros do modelo, dado que não é passado explicitamente ao algoritmo de ajuste da função. Para definir estes dois valores, inclua o seguinte no cimo do seu ficheiro de código:

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

substituindo o `[num1]` pelo número de parâmetros do modelo e o `[num2]` pelo número máximo de iterações a efectuar.

### Implementar as Funções Necessárias

Para usar os ficheiros de inclusão para os ajustes não-lineares, o utilizador deverá fornecer a função a usar como o modelo, as derivadas parciais da função respeitantes a cada parâmetro e as estimativas iniciais dos parâmetros de ajuste óptimo. Para o fazer, deverão ser implementadas três funções. Estas estão descritas em baixo.

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

  Esta função calcula o valor do Y do modelo de ajuste para um dado valor de X `dX`, usando a lista fornecida de parâmetros `pdParameters`. A ordem dos parâmetros em `pdParameters` é arbitrária, mas deverá ser consistente com as outras duas funções implementadas. Por exemplo, para um modelo exponencial, o `function_calculate` poderia ser implementado da seguinte forma:

      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` )`

  Esta função calcula as derivadas parciais da função do modelo para um valor indicado para o X, o `dX`. As derivadas parciais deverão ser devolvidas em `pdDerivatives`. A ordem das derivadas parciais na lista `pdDerivatives` deverá corresponder à ordem dos parâmetros em `pdParameters` (i.e. se o `pdParameters[0]` contiver o parâmetro 'lambda' de um modelo exponencial, o `pdDerivatives[0]` deverá conter a derivada do modelo respeitante ao 'lambda').

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

  Esta função oferece uma estimativa inicial dos parâmetros de ajuste óptimos para a função de ajuste. A lista de valores em X e Y dos pontos de dados são oferecidas em `pdX` e `pdY` respectivamente e o número de pontos de dados é indicado em `iLength`. Você poderá usar qualquer um ou mesmo nenhum destes parâmetros à sua discrição para calcular a estimativa inicial. A função deverá colocar as estimativas iniciais calculadas em `pdParameterEstimates`, com a ordem das estimativas correspondente à ordem dos parâmetros em `pdParameters` do `function_calculate` e `function_derivative`. Tenha em mente que a estimativa inicial é importante para determinar se a função de ajuste converge para uma solução ou não.

### Invocar as Funções de Ajuste

Logo que todas as funções necessárias tenham sido implementadas, a função de ajuste do ficheiro de inclusão usado poderá ser chamada:

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

ou

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

dependendo se você está ou não a usar um ajuste ponderado.

A função irá devolver `0` em caso de sucesso ou `-1` em caso de erro, por isso é mais simples atribuir o valor devolvido pela função em C exportada como sendo igual ao valor devolvido pela função de ajuste. Para manter a simplicidade, o código do 'plugin' poderá simplesmente passar os argumentos indicados à função em C exportada para a função de ajuste. Repare, contudo, que o 'inArrays' deverá estar estruturado da seguinte forma:

- O `inArrays[0]` deverá conter a lista de coordenadas em X dos pontos de dados.

- O `inArrays[1]` deverá conter a lista de coordenadas em Y dos pontos de dados

- O `inArrays[2]` só existe se o `kstfit_linear_weighted` for invocado e deverá conter a lista de pesos a usar como ponderação para o ajuste.

A forma mais simples de garantir que o 'inArrays' está estruturado correctamente é indicar a ordem correcta dos vectores de entrada no ficheiro XML para o 'plugin'.

Depois de o `kstfit_linear_unweighted` ou o `kstfit_linear_weighted` terem sido chamados, o `outArrays` e o `outScalars` serão definidos da seguinte forma:

- O `outArrays[0]` irá conter a lista de valores ajustados em Y.

- O `outArrays[1]` irá conter a lista dos valores residuais.

- O `outArrays[2]` irá conter a lista dos parâmetros de ajuste óptimo que foram estimados.

- O `outArrays[3]` irá conter a matriz de covariância, devolvida linha após linha numa lista.

- O `outScalars[0]` irá conter o chi^2/nu, em que o chi^2 é a soma ponderada dos quadrados dos valores residuais, enquanto o nu é o número de graus de liberdade.

O `outArrayLens` será configurado correctamente para indicar o tamanho de cada lista à saída.

Garanta que os resultados indicados no ficheiro XML correspondem aos que a função em C exportada devolve (o que, na maioria dos casos, será simplesmente os valores à saída devolvidos pela função de ajuste).

### Exemplo

Segue-se um exemplo de um 'plugin' de ajuste não-linear que efectue um ajuste a um modelo exponencial.

    /*
     *  'Plugin' de ajuste exponencial para o KST.
     *  Copyright 2004, The University of British Columbia
     *  Lançado segundo os termos da 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_exponential(const double *const inArrays[], const int inArrayLens[],
                    const double inScalars[],
                    double *outArrays[], int outArrayLens[],
                    double outScalars[]);

    int kstfit_exponential(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 );
    }

## Criar 'Plugins' para Filtros

O &kst; oferece ficheiros de inclusão para simplificar a implementação de filtros. A utilização destes ficheiros de inclusão está descrita em baixo.

### Ficheiros de Inclusão

O ficheiro de inclusão para a implementação de filtros está localizada na pasta `kst/plugins/pass_filters` do pacote de código do &kst;. O ficheiro chama-se `filters.h`. Para usar este ficheiro, inclua-o no ficheiro de código-fonte do seu 'plugin':

    #include <../filters.h>

(por convenção, será colocado o ficheiro de código-fonte para o 'plugin' uma pasta abaixo do local em que se encontram os ficheiros de inclusão).

### Funções Necessárias

O ficheiro de inclusão `filters.h` contém uma única função que calcula a transformada de Fourier para uma dada função, aplica o filtro fornecido a essa transformações e depois calcula a transformada inversa de Fourier para o sinal previamente transformado. Para fornecer o filtro, a seguinte função deverá ser implementada no código-fonte do seu 'plugin':

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

Esta função deverá calcular a amplitude filtrada para a frequência `dFreqValue`. O `inScalars[]` irá conter os valores escalares inalterados para o 'plugin', indicados no ficheiro XML. Muito provavelmente, o `inScalars[]` irá conter as frequências de cortes ou outras propriedades do filtro. Por exemplo para implementar um filtro Butterworth passa-alto, o `filter_calculate` poderia ser implementado da seguinte forma:

    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;
    }

### Chamar a Função de Filtragem

Logo que o `filter_calculate` necessário tenha sido implementado, a função de filtragem do ficheiro de inclusão poderá ser invocada:

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

Os argumentos fornecidos à função em C exportada poderão ser normalmente passados ao `kst_pass_filter` sem nenhuma modificação. Contudo, existem algumas restrições para os argumentos:

- O `inArrays[0]` precisa de conter a lista de dados a filtrar.

- O `inScalars` deverá conter os parâmetros específicos do filtro a serem usados pela função `filter_calculate`.

Depois da chamada da função, o `outArrays[0]` irá conter a lista de dados filtrada e o `outArrayLens` será configurado convenientemente. A função `kst_pass_filter` não usa o `outScalars`.

### Exemplo

Segue-se um exemplo de um 'plugin' de um filtro passa-alto Butterworth.

    /*
     *  'Plugin' de um filtro passa-baixo Butterworth para o KST.
     *  Copyright 2004, The University of British Columbia
     *  Lançado segundo os termos da 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;
    }