# Creando plugins adicionales

&kst; posee una interfaz simple y estándar que facilita la creación de plugins adicionales. Además de detallar los requerimientos básicos para desarrollar plugins, las secciones siguientes describen como hacer uso de archivos de cabecera ya escritos para crear ciertos tipos de plugins.

## Creando un plugin básico

Un plugin de &kst; consiste de dos archivos—un archivo XML y una librería dinámica.

### El archivo XML

El archivo XML proporciona información sobre el plugin y describe sus entradas y salidas. Este es un ejemplo de un archivo XML usado como plugin de &kst;:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE Module SYSTEM "file:/Repository/Level2/Soft/ProC/moduledef.dtd">

    <module>

    <intro>
    <modulename name="testplugin"/>    <!-- El nombre del módulo -->
    <author name="Rick Chern"/> <!-- El nombre del autor -->
    <description text="Un plugin de pruebas para mí"/> <!-- Descripción del  módulo -->
    <version minor="1" major="0"/>  <!-- Número de versión del módulo -->
    <state devstate="release"/>     <!-- Estado de desarrolo del módulo (opcional)-->
    </intro>

    <interface>

    <!--inputs here-->
    <input>
    <table type="float" name="Input Vector 1" descr="The first input vector" />
    </input>

    <input>
    <float name="Input Scalar 1" descr="The first input scalar" />
    </input>

    <!--outputs here-->
    <output>
    <table type="float" name="Output Vector 1" descr="The first output vector" />
    </output>

    <output>
    <float name="Output Scalar 1" descr="The first output scalar" />
    </output>

    </interface>

    </module>

En general, puede usar el ejemplo de arriba como plantilla y modificar sus secciones para ajustarse a sus necesidades. Como puede verse en el ejemplo, el archivo XML consiste de un nodo `module`. El nodo `module` tiene un nodo `intro` y un nodo `interface`. Debería modificar el nodo `intro` usando como guía los comentarios dados en el archivo XML. Es importante que en `modulename`, el atributo `name` debe corresponderse al nombre de la librería dinámica que va a usar.

El nodo `interface` describe las entradas y salidas actuales del plugin. Cada entrada se describe por un nodo `input`, y cada salida se describe con un nodo `output`. Cada input u output deberían tener como nodos hijos o bien `table` o `float`. El atributo `type` de `table` debe ponerse como `"float"`. Notar que el orden de las inputs y de las outputs es importante—tal orden se usa para determinar los valores del index para cada conjunto de datos de entrada y salida de la librería dinámica, y es el mismo orden que el utilizado para mostrar los campos de entrada y salida de la interfaz de plugin de &kst;.

Una vez que haya completado el archivo XML, guardelo como `[modulename].xml`, donde `[modulename]` es el valor dado al atributo `name` del nodo `modulename`.

### La librería dinámica

La librería dinámica contiene la funcionalidad actual del plugin. En otras palabras, determina como derivar las salidas de unas entradas dadas. A continuación se muestran los requerimientos para una librería dinámica:

- El objeto debe exportar el siguiente símbolo en C para el enlace:

      int symbol(const double *const inArrays[],
                 const int inArrayLens[],
                 const double inScalars[],
                 double *outArrays[],
                 int outArrayLens[],
                 double outScalars[])

  donde `symbol` debe corresponder al valor del atributo `name` del nodo `modulename` en el archivo XML. Esta es la única función que será llamada por &kst; (aunque usted pueda tener otras funciones en su plugin). A continuación se describen los argumentos de esta función:

  `const double *const inArrays[]`

- El vector de vectores de entrada. Cada vector de entrada (aquí) se corresponderá con un vector de entrada (en &kst;). Los vectores deben estar en el mismo orden que los vectores definidos en el archivo XML, de tal forma que `inArrays[0]` sea el vector que represente al primer vector de entrada, `inArrays[1]` sea el vector que represente al segundo vector de entrada, y así sucesivamente.

`const int inArraysLens[]`

- El vector conteniendo las longitudes de cada vector de entrada. Las longitudes se guardan en el mismo orden que los vectores en `inArrays[]` (es decir, `inArrayLens[0]` es la longitud de `inArrays[0]`).

`const double inScalars[]`

- El vector de escalares de entrada. Los escalares se guardan en el mismo orden que están definidos en el archivo XML.

`double *outArrays[]`

- El vector de vectores de salida. Cada vector de salida (aquí) corresponde a un vector de salida en kst, y los vectores deben estar en el mismo orden que los vectores de salida listados en el archivo XML.

`int outArrayLens[]`

- El vector que debería contener las longitudes de los vectores de salida. Tales longitudes deben almacenarse en el mismo orden que los vectores en `outArrays[]`.

`double outScalars[]`

- El vector de escalares de salida. Los escalares deben estar en el mismo orden que su listado en el archivo XML.

- La función debe retornar `0` si se ha ejecutado con éxito, y `-1` si no.

- El código de la librería debe ser capaz de trabajar con entradas inesperadas, tales como vectores vacíos (en la mayoría de los casos, es suficiente con devolver un valor de `-1` cuando se presentan tales casos).

- El número y el tipo de las salidas debe ser exactamente como el especificado en en archivo XML.

- Probablemente necesitará redimensionar los vectores en `outArrays[]`. Para ello, use la función `realloc()`. Por ejemplo,

      outArrays[0]=(double*)realloc(outArrays[0], 5*sizeof(double));

  va a reservar un espacio de 5 "doubles" para `outArrays[0]`. Use solo `realloc()` para reservar memoria.

- Los argumentos de entrada deben permanecer constantes. No haga un "cast" a tipos no constantes.

A continuación se muestra un ejemplo de un archivo con el código de una librería dinámica que es un plugin simple:

    #include <stdlib.h>

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

    int testplugin(const double *const inArrays[], const int inArrayLens[],
                    const double is[],
                    double *outArrays[], int outArrayLens[],
                    double outScalars[])

    //Acepta 1 vector y 1 escalar. Multiplica todos los elementos del vector por el
    //escalar, y da como salida el vector resultante. También da como salida el escalar original.
    {
            // Asigna las salidas (outputs)
            outArrayLens[0]=inArrayLens[0];

            //Redimensiona los vectores de salida
            outArrays[0]=(double*)realloc(outArrays[0], inArrayLens[0]*sizeof(double));

            //multiplica cada elemento del vector de entrada
            //con el escalar
            for (int i=0; i<inArrayLens[0]; i++)
            {
                    outArrays[0][i]=inArrays[0][i] * is[0];
            }

            //ahora, asignar el escalar de salida
            outScalars[0]=is[0];

            return 0;
    }

### Compilando el plugin

Si usa &gcc; para compilar su plugin, simplemente compile para obtener el archivo objeto:

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

y entonces, cree la librería dinámica:

    ld -o myplugin.so -shared myplugin.o

Los archivos resultantes `*.so` y `*.xml` deben ponerse en el mismo directorio. Cuando use el gestor de plugins de &kst; para cargar el archivo XML, buscará automáticamente por la librería dinámica en el mismo directorio.

## Creando plugins para ajustes lineales

Para crear un plugin para un ajuste lineal, usted podría implementar sus propios algoritmos de ajuste y retornar con los vectores apropiados. Sin embargo, &kst; ya viene con archivos de cabecera que le facilitan la implementación de plugins para ajuste por mínimos cuadrados con tal de que codifique unas pocas funciones. Esta sección le muestra como sacar partido a tales archivos.

### Archivos de cabecera

Se suministran dos archivos de cabecera para hacer ajustes lineales, `linear.h` (para ajustes sin ponderación) y `linear_weighted.h` (para ajustes ponderados). Ambos se encuentran en las fuentes de &kst; bajo el directorio `kst/plugins/fits/`. Para usar tales archivos, incluya solo uno de ellos en el código fuente de su plugin:

    #include <../linear.h>

o

    #include <../linear_weighted.h>

(por convención, vamos a poner el código fuente del plugin en un directorio más abajo que el que contiene los archivos de cabecera).

### Implementando las funciones requeridas

Dar un modelo lineal general:

donde `y` es un vector de `n` observaciones, `X` es una matriz `n` por `p` de variables de predicción, y `c` es el vector de `p` parámetros de mejor ajuste que van a estimarse, los archivos de cabecera dan funciones para la estimación de `c` para un `y` y un `X` dados. Para dar `X`, esta función debe implementarse en el código del plugin:

    double calculate_matrix_entry( double dX, int iPos )

Esta función debe retornar el valor de la entrada en la columna `iPos` de la matriz de variables de predicción, usando el valor de `x` `dX`. Esta función la llamará linear.h o linear_weighted.h. La implementación de esta función depende del modelo que desee usar para el ajuste, y es única para cada plugin de ajuste lineal. Por ejemplo, para ajustar a un modelo polinomial, `calculate_matrix_entry` debe implementarse como sigue:

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

### Llamando las funciones de ajuste

Una vez que se han incluido los archivos de cabecera y que se ha implementado la función `calculate_matrix_entry`, llamar la función de ajuste apropiada incluida desde el archivo de cabecera:

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

o

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

Cada función retornará `0` en éxito, y `-1` en error, así que es una buena idea que el valor de retorno de la función C exportada sea igual al valor de retorno de la función de ajuste. Para mantener la simplicidad, el código para el plugin puede simplemente pasar los argumentos dados a la función C exportada a la función de ajuste. Notar, sin embargo, que la variable inArrays debe estructurarse de esta forma:

- `inArrays[0]` debe contener un vector de coordenadas x de puntos de datos

- `inArrays[1]` debe contener un vector de coordenadas y de puntos de datos

- `inArrays[2]` solo existe si se llama la función `kstfit_linear_weighted`, y debe contener un vector con los valores de los pesos usados para el ajuste.

La manera más sencilla de asegurar que inArrays está estructurado correctamente es especificando el orden correcto de vectores de entrada (input) en el archivo XML para el plugin.

`iNumParams` es el numero de parámetros en el modelo usado, el cual debe ser igual al número de columnas en la matriz `X` de variables de predicción. `iNumParams` debe estar correcto antes que la función de ajuste se llame.

Después de llamar `kstfit_linear_unweighted` o `kstfit_linear_weighted`, las variables `outArrays` y `outScalars` serán asignadas como sigue:

- `outArrays[0]` contendrá el vector con los valores y ajustados.

- `outArrays[1]` contendrá el vector con los residuos.

- `outArrays[2]` contendrá el vector con los valores de los parámetros calculados de mejor ajuste.

- `outArrays[3]` contendrá la matriz de covariancia, retornada fila a fila en un vector.

- `outScalars[0]` contendrá chi^2/nu, donde chi^2 es la suma ponderada de los cuadrados de los residuos, y nu son los grados de libertad.

`outArrayLens` se asignará correctamente para indicar la longitud de cada vector de salida.

Asegúrese de que cada salida especificada en el archivo XML se corresponde a aquellas retornadas por la función C exportada (que en la mayoría de los casos será la salida retornada por la función de ajuste).

### Ejemplo

A continuación se muestra un ejemplo de código de un plugin para un ajuste lineal.

    /*
     *  Plugin de ajuste polinomial  para 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;
    }

## Creando plugins para ajustes no lineales

&kst; proporciona archivos de cabecera diseñados para simplificar la creación de plugins para ajustes no lineales por mínimos cuadrados. La sección siguiente detalla como usar tales archivos de cabecera.

### Archivos de cabecera y definiciones

Los archivos de cabecera para ajustes no lineales están situados en el directorio `kst/plugins/fits_nonlinear` de las fuentes de &kst;. Los archivos se llaman `non_linear.h` y `non_linear_weighted.h` para ajustes normales y ponderados, respectivamente. Para usar estos archivos, incluya solo uno de ellos en el código de su plugin:

    #include <../non_linear.h>

o

    #include <../non_linear_weighted.h>

(por convención, vamos a poner el código fuente del plugin en un directorio más abajo que el que contiene los archivos de cabecera).

Como un ajuste no lineal es un proceso iterativo, debe definir el número máximo de iteraciones que deben hacerse. El algoritmo de ajuste no lineal se detendrá cuando al menos una de las condiciones siguientes sea verdad:

- Se ha alcanzado el máximo número de iteraciones.

- Se ha alcanzado una precisión de 10-4.

Además, necesita definir el número de parámetros en el modelo, pues no se pasa de forma explícita a la función de ajuste. Para definir estos dos valores, incluya lo siguiente en la parte superior de su código:

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

reemplazando `[num1]` con el número de parámetros en el modelo, y `[num2]` con el número máximo de iteraciones a realizar.

### Implementando las funciones requeridas

Para usar los archivos de cabecera para ajustes no lineales, debe proveer una función a usar como modelo, las derivadas parciales de la función con respecto a cada parámetro, y las estimaciones iniciales de los parámetros de mejor ajuste. Para hacer esto, deben implementarse 3 funciones. Estas funciones se describen más abajo.

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

- Esta función calcula el valor y del modelo de ajuste para un valor x dado `dX`, usando el vector de parametros `pdParameters` pasado a la función. El orden de los parametros en `pdParameters` es arbitrario, pero debe ser consistente con el orden en las otras dos funciones implementadas. Por ejemplo, para un modelo exponencial, se puede implementar `function_calculate` como sigue:

      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 función calcula las derivadas parciales de la función modelo para un valor de x dado `dX`. Las derivadas parciales deben retornarse en `pdDerivatives`. El orden de lasderivadas parciales en el vector `pdDerivatives` debe corresponderse al orden de los parámetros en `pdParameters` (esto es, si `pdParameters[0]` contiene el parámetro lambda para un modelo exponencial, `pdDerivatives[0]` debe contener un modelo de la derivada del modelo con respecto a lambda).

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

- Esta función da una estimación inicial de los parámetros de mejor ajuste para la función de ajuste. El vector de valores x e y de puntos de datos se pasan en `pdX` y `pdY` respectivamente, y el número de puntos de datos es suministrado por `iLength`. Puede usar o no cualquiera de estos parámetros para calcular una estimación inicial. La función debe poner las estimación inicial calculada en `pdParameterEstimates`, con el orden de las estimaciones correspondiéndose al orden de los parametros en `pdParameters` de `function_calculate` y `function_derivative`. Tenga presente que la estimación inicial es importante en la determinación de si la función de ajuste converge o no a una solución.

### Llamando las funciones de ajuste

Una vez que todas las funciones requeridas están implementadas, se puede llamar a la función de ajuste del archivo de cabecera incluido:

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

o

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

dependiendo en si se implementa un ajuste normal o ponderado.

Cada función retornará `0` en éxito, y `-1` en error, así que es una buena idea que el valor de retorno de la función C exportada sea igual al valor de retorno de la función de ajuste. Para mantener la simplicidad, el código para el plugin puede simplemente pasar los argumentos dados a la función C exportada a la función de ajuste. Notar, sin embargo, que la variable inArrays debe estructurarse de esta forma:

- `inArrays[0]` debe contener un vector de coordenadas x de puntos de datos

- `inArrays[1]` debe contener un vector de coordenadas y de puntos de datos

- `inArrays[2]` solo existe si se llama la función `kstfit_linear_weighted`, y debe contener un vector con los valores de los pesos usados para el ajuste.

La manera más sencilla de asegurar que inArrays está estructurado correctamente es especificando el orden correcto de vectores de entrada (input) en el archivo XML para el plugin.

Después de llamar `kstfit_linear_unweighted` o `kstfit_linear_weighted`, las variables `outArrays` y `outScalars` serán asignadas como sigue:

- `outArrays[0]` contendrá el vector con los valores y ajustados.

- `outArrays[1]` contendrá el vector con los residuos.

- `outArrays[2]` contendrá el vector con los valores de los parámetros calculados de mejor ajuste.

- `outArrays[3]` contendrá la matriz de covariancia, retornada fila a fila en un vector.

- `outScalars[0]` contendrá chi^2/nu, donde chi^2 es la suma ponderada de los cuadrados de los residuos, y nu son los grados de libertad.

`outArrayLens` se asignará correctamente para indicar la longitud de cada vector de salida.

Asegúrese de que cada salida especificada en el archivo XML se corresponde a aquellas retornadas por la función C exportada (que en la mayoría de los casos será la salida retornada por la función de ajuste).

### Ejemplo

Lo que sigue es un ejemplo de un plugin para ajustes no lineales que hace un ajuste según un modelo exponencial.

    /*
     *  Plugin de ajuste exponencial para 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_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 );
    }

## Creando plugins de filtro de pasos

&kst; proporciona archivos de cabecera diseñados para simplificar la creación de plugins para filtros de paso. A continuación se detalla como usar tales archivos de cabecera.

### Archivos de cabecera

El archivo de cabecera para filtros de paso está situado en el directorio `kst/plugins/pass_filter` de las fuentes de &kst;. El archivo se llama `filters.h`. Para usar este archivo, incluyálo en el código de su plugin:

    #include <../filters.h>

(por convención, vamos a poner el código fuente del plugin en un directorio más abajo del que contiene los archivos de cabecera).

### Funciones requeridas

El archivo de cabecera `filters.h` contiene una función simple que calcula la transformada de Fourier de una función dada, aplica el filtro suministrado a la transformada de Fourier, y luego calcula la transformada inversa de Fourier en la transformada de Fourier ya filtrada. Para suministrar el filtro, se necesita implementar la función siguiente en el código del plugin:

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

Esta función debe calcular la amplitud filtrada para la frecuencia `dFreqValue`. `inScalars[]` contendrá los escalares inalterados para el plugin, específicados en el archivo XML. Lo más probable es que `inScalars[]` contenga las frecuencias de corte u otras propiedades del filtro. Por ejemplo, para implementar un filtro de paso alto Butterworth, se puede implementar `filter_calculate` como sigue:

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

### Llamando a la función de filtrado

Una vez que se ha implementado la función `filter_calculate`, se puede llamar la función de filtrado del archivo de cabecera:

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

Los argumentos suministrados a la función C exportada pueden pasarse a `kst_pass_filter` sin modificarse. Sin embargo, hay unas cuantas restricciones en los argumentos:

- `inArrays[0]` debe contener el vector de datos a filtrar.

- `inScalars` debe contener los parámetros específicos del filtro que se usarán por la función `filter_calculate`.

Despues de la llamada de la función, `outArrays[0]` contendrá un vector de datos filtrados, y se asignará `outArrayLens` de manera consecuente. La función `kst_pass_filter` no usa `outScalars`.

### Ejemplo

A continuación se muestra un ejemplo de un filtro de paso que implementa el filtro de paso alto de Butterworth.

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