# Skapa ytterligare insticksprogram

&kst; har ett enkelt och standardiserat gränssnitt som tillåter att enkelt skapa ytterligare insticksprogram. Förutom att ge detaljer om grundläggande krav för insticksprogram, beskriver följande avsnitt hur färdigskrivna deklarationsfiler kan användas för att skapa vissa sorters insticksprogram.

## Skapa ett enkelt insticksprogram

Ett &kst;-insticksprogram består av två filer: en XML-fil och en delad objektfil.

### XML-filen

XML-filen tillhandahåller information om insticksprogrammet och beskriver dess indata och utdata. Det följande är ett exempel på en XML-fil för ett &kst;-insticksprogram:

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

    <module>

    <intro>
    <modulename name="testplugin"/>    <!-- Modulens namn -->
    <author name="Rick Chern"/> <!-- Författarens namn -->
    <description text="Ett testinsticksprogram för mig"/> <!-- En beskrivning av modulen -->
    <version minor="1" major="0"/>  <!-- Modulens versionsnummer -->
    <state devstate="release"/>     <!-- Utvecklingstillståndet för modulen (valfritt)-->
    </intro>

    <interface>

    <!--inputs here-->
    <input>
    <table type="float" name="Input Vector 1" descr="Den första indatavektorn" />
    </input>

    <input>
    <float name="Input Scalar 1" descr="Den första indataskalären" />
    </input>

    <!--outputs here-->
    <output>
    <table type="float" name="Output Vector 1" descr="Den första utdatavektorn" />
    </output>

    <output>
    <float name="Output Scalar 1" descr="Den första utdataskalären" />
    </output>

    </interface>

    </module>

I allmänhet kan du använda exemplet ovan som en mall och ändra delar för att passa dina behov. Som syns i exemplet, består XML-filen av noden `module`. Noden `module` innehåller noderna `intro` och `interface`. Du måste ändra noden `intro` på lämpligt sätt genom att använda kommentarerna i XML-filen ovan som guider. Det är viktigt att `modulename` har egenskapen `name` satt till samma namn som den delade objektfilen kommer att använda.

Noden `interface` beskriver insticksprogrammets verkliga indata och utdata. Varje indatavärde beskrivs av noden `input`, och varje utdatavärde beskrivs av noden `output`. Varje indatavärde eller utdatavärde ska antingen ha delnoden `table` eller `float`. Egenskapen `type` i en `table` måste vara satt till `"float"`. Observera att ordningen för indata och utdata spelar roll: Ordningen används för att avgöra indexvärdet för varje indatavärde och utdatavärde i objektfilen, och är samma ordning som indatafälten och utdatafälten visas i gränssnittet för insticksprogram i &kst;.

När du väl är klar med XML-filen, spara den som `[modulnamn].xml`, där `[modulnamn]` är värdet på egenskapen `name` i noden `modulename`.

### Den delade objektfilen

Den delade objektfilen innehåller insticksprogrammets själva funktion. Med andra ord avgör den hur utdata ska skapas från given indata. Följande krav gäller för den delade objektfilen:

- Objektet måste exportera följande C-länksymboler:

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

  där symbol måste vara värdet på egenskapen `name` i noden `modulename` i XML-filen. Det här är den enda funktionen som kommer att anropas av &kst; (även om du kan ha flera funktioner). Det följande beskriver varje argument till funktionen:

  `const double *const inArrays[]`

    Fältet med indatafält. Varje indatafält motsvarar en indatavektor. Fälten är i samma ordning som vektorerna listas i XML-filen, så `inArrays[0]` är fältet som representerar den första indatavektorn, `inArrays[1]` är fältet som representerar den andra indatavektorn, och så vidare.

  `const int inArraysLens[]`

    Fältet som innehåller fältlängderna för varje indatafält. Längderna lagras i samma ordning som fälten i `inArrays[]` (t.ex. är `inArrayLens[0]` längden på `inArrays[0]`).

  `const double inScalars[]`

    Fältet med indataskalärer. Skalärerna lagras i samma ordning som de listas i XML-filen.

  `double *outArrays[]`

    Fältet med utdatafält. Varje utdatafält motsvarar en utdatavektor, och fälten ska vara i samma ordning som utdatavektorerna listas i XML-filen.

  `int outArrayLens[]`

    Fältet som ska innehålla längderna för utdatafälten. Längderna ska lagras i samma ordning som i fälten i `outArrays[]`.

  `double outScalars[]`

    Fältet med utdataskalärer. Skalärerna ska vara i samma ordning som de listas i XML-filen.

- Funktionen måste returnera `0` om den utfördes med lyckat resultat, och `-1` annars.

- Koden i objektfilen måste hantera oväntad indata, som tomma indatafält (i de flesta fall är returvärdet `-1` tillräckligt när sådana situationer uppstår).

- Antal och typ av utdata måste vara exakt som det anges i XML-filen.

- Du måste troligen ändra storlek på fälten i `outArrays[]`. För att göra det, använd funktionen `realloc()`. Till exempel

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

  allokerar utrymme för fem dubbla flyttal i `outArrays[0]`. Använd inte någon annan minnesallokering än `realloc()`.

- Indataargumenten måste förbli konstanta. Konvertera dem inte till icke-konstanta typer.

Följande är ett exempel på den delade objektfilens källkod för ett enkelt insticksprogram:

    #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[])

    //Accept 1 vector and 1 scalar. Multiply all elements of the vector by the
    //scalar, and output the resulting vector. Also output the original scalar.
    {
            //Set the outputs
            outArrayLens[0]=inArrayLens[0];

            //resize the output arrays
            outArrays[0]=(double*)realloc(outArrays[0], inArrayLens[0]*sizeof(double));

            //multiply each element of the input vector
            //by the scalar
            for (int i=0; i<inArrayLens[0]; i++)
            {
                    outArrays[0][i]=inArrays[0][i] * is[0];
            }

            //now set the output scalar
            outScalars[0]=is[0];

            return 0;
    }

### Kompilera insticksprogrammet

Om du använder &gcc; för att kompilera insticksprogrammet, kompilera helt enkelt objektfilen:

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

och skapa därefter det delade biblioteket:

    ld -o myplugin.so -shared myplugin.o

Resulterande filerna `*.so` och `*.xml` måste placeras i samma katalog. När du använder instickshanteringen i &kst; för att ladda XML-filen, letar den automatiskt efter den delade objektfilen i samma katalog.

## Skapa insticksprogram för linjäranpassning

För att skapa ett insticksprogram för linjäranpassning, skulle du kunna implementera dina egna anpassningsalgoritmer och ge lämpliga vektorer som utdata. &kst; levereras dock redan med deklarationsfiler som gör det enkelt för dig att implementera insticksprogram för linjär minsta kvadratanpassning genom att bara tillhandahålla några få funktioner. Det här avsnittet beskriver hur man drar nytta av dessa filer.

### Deklarationsfiler

Två deklarationsfiler tillhandahålls för att utföra linjäranpassningar, `linear.h` (för oviktade linjäranpassningar) och `linear_weighted.h` (för viktade linjäranpassningar). Båda finns under `kst(plugins/fits/` i källkodsarkivet för &kst;. För att använda filerna, inkludera bara en av dem i ditt insticksprograms källkod:

    #include <../linear.h>

eller

    #include <../linear_weighted.h>

(som konvention placerar vi källkoden för insticksprogrammet en katalognivå under platsen där deklarationsfilerna finns).

### Implementera nödvändiga funktioner

Given en allmän linjär modell:

där `y` är en vektor med `n` observationer, `X` är en matris med storleken `n` gånger `p` med prediktorvariabler, och `c` är vektorn med `p` parametrar för bästa anpassning som ska uppskattas. Deklarationsfilen tillhandahåller funktioner för att uppskatta `c` för ett givet `y` och `X`. För att tillhandahålla `X`, måste följande funktion implementeras i insticksprogrammets källkod:

    double calculate_matrix_entry( double dX, int iPos )

Funktionen ska returnera värdet på resultatet i kolumn `iPos` från matrisen med prediktorvariabler, genom att använda `x`-värdet `dX`. Funktionen anropas av linear.h eller linear_weighted.h. Implementeringen av funktionen beror på modellen du vill använda för anpassningen, och är unik för varje insticksprogram för linjäranpassning. För att till exempel anpassa till en polynommodell, skulle funktionen `calculate_matrix_entry` kunna implementeras på följande sätt:

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

### Anropa anpassningsfunktionerna

När lämplig deklarationsfil har inkluderats och funktionen `calculate_matrix_entry` har implementerats, anropa lämplig anpassningsfunktion som inkluderats från deklarationsfilen:

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

eller

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

Varje funktion returnerar `0` när de lyckas, eller `-1` vid fel, så det är en god idé att låta returvärdet för den exporterade C-funktionen vara samma som returvärdet för anpassningsfunktionen. För att hålla det enkelt, kan koden för insticksprogrammet helt enkelt skicka vidare argumenten som ges till den exporterade C-funktionen till anpassningsfunktionen. Observera dock att inArrays måste ha följande struktur:

- `inArrays[0]` måste innehålla fältet med x-koordinater för datapunkterna

- `inArrays[1]` måste innehålla fältet med y-koordinater för datapunkterna

- `inArrays[2]` finns bara om `kstfit_linear_weighted` anropas, och måste innehålla fältet med viktningar för anpassningen.

Det enklaste sättet att försäkra att inArrays är riktigt strukturerad är att ange rätt ordning för indatavektorer i insticksprogrammets XML-fil.

`iNumParams` är antalet parametrar som ingår i den använda anpassningsmodellen, vilket ska vara lika med antal kolumner i matrisen `X` med prediktorvariabler. `iNumParams` måste anges riktigt innan anpassningsfunktionen anropas.

Efter funktionen `kstfit_linear_unweighted` eller `kstfit_linear_weighted` anropas, har `outArrays` och `outScalars` tilldelats följande:

- `outArrays[0]` kommer att innehålla fältet med anpassade y-värden.

- `outArrays[1]` kommer att innehålla fältet med rester.

- `outArrays[2]` kommer att innehålla fältet med uppskattade parametrar för bästa anpassning.

- `outArrays[3]` kommer att innehålla kovariansmatrisen, returnerad rad för rad i fältet.

- `outScalars[0]` kommer att innehålla χ²/ν, där χ² är viktad summa av resternas kvadrater, och ν är frihetsgraden.

`outArrayLens` kommer att har rätt värde för att ange längden för varje utdatafält.

Försäkra dig om att angiven utdata i XML-filen passar ihop med den som de exporterade C-funktionerna returnerar (vilket i de flesta fall helt enkelt är utdata från anpassningsfunktionen).

### Exempel

Det följande är ett exempel på källkoden för ett insticksprogram för linjäranpassning.

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

## Skapa insticksprogram för icke-linjär anpassning

&kst; tillhandahåller deklarationsfiler för att förenkla implementeringen av insticksprogram med icke-linjära minsta kvadrat anpassning. Användningen av deklarationsfilerna beskrivs nedan.

### Deklarationsfiler och definitioner

Deklarationsfilerna för icke-linjär anpassning finns under `kst(plugins/fits_nonlinear/` i källkodsarkivet för &kst;. Filerna heter `non_linear.h` och `non_linear_weighted.h` för respektive oviktade och viktade anpassningar. För att använda filerna, inkludera bara en av dem i ditt insticksprograms källkod:

    #include <../non_linear.h>

eller

    #include <../non_linear_weighted.h>

(som konvention placerar vi källkoden för insticksprogrammet en katalognivå under platsen där deklarationsfilerna finns).

Eftersom icke-linjär anpassning är en iterativ process, måste du också definiera maximalt antal iterationer som ska utföras. Algoritmen för icke-linjär anpassning stoppar när minst ett av följande villkor är sant:

- Maximalt antal iterationer har uppnåtts.

- Precisionen 10-4 har uppnåtts.

Dessutom måste du definiera antal parametrar i modellen, eftersom det inte explicit skickas till anpassningsfunktionen. För att definiera dessa två värden, inkludera följande längst upp i din källkod:

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

som ersätter `[num1]` med antal parametrar i modellen, och `[num2]` med maximalt antal iterationer att utföra.

### Implementera nödvändiga funktioner

För att använda deklarationsfilen för icke-linjär anpassning, måste du tillhandahålla funktionen som ska användas som modell, partiella derivatorna för funktionen med avseende på varje parameter och ursprungliga uppskattningar för parametrarna för bästa anpassning. För att göra det måste tre funktioner implementeras. Dessa funktioner beskrivs nedan.

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

  Den här funktionen beräknar Y-värdet för anpassningsmodellen för ett givet X-värde `dX`, med det tillhandahållna fältet med parametrar `pdParameters`. Parametrarnas ordning i `pdParameters` är godtycklig, men måste stämma med de två andra implementerade funktionerna. Till exempel för en exponentiell modell, skulle `function_calculate` kunna implementeras på följande sätt:

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

  Den här funktionen beräknar partiella derivatorna för modellfunktionen för ett givet X-värde `dX`. De partiella derivatorna ska returneras i `pdDerivatives`. Ordningen av de partiella derivatorna i fältet `pdDerivatives` ska motsvara ordningen av parametrarna i `pdParameters` (dvs. om `pdParameters[0]` innehåller parametern lambda för en exponentialmodell, ska `pdDerivatives[0]` innehålla derivatan för modellen med avseende på lambda).

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

  Den här funktionen tillhandahåller en ursprunglig uppskattning av parametrarna för bästa anpassning till anpassningsfunktionen. Fälten med X- och Y-värden för datapunkterna tillhandahålls i respektive `pdX` och `pdY`, och antal datapunkter anges av `iLength`. Du kan använda alla eller ingen av parametrarna enligt eget tycke för att beräkna den ursprungliga uppskattningen. Funktionen ska placera de beräknade ursprungliga uppskattningarna i `pdParameterEstimates`, och låta uppskattningarnas ordning stämma med ordningen för parametrarna i `pdParameters` i `function_calculate` och `function_derivative`. Kom ihåg att den ursprungliga uppskattningen är viktig för att avgöra om anpassningsfunktionen konvergerar till en lösning.

### Anropa anpassningsfunktionerna

När alla nödvändiga funktioner har implementerats, kan anpassningsfunktionen från den inkluderade deklarationsfilen anropas:

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

eller

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

beroende på om du implementerar en oviktad anpassning eller en viktad anpassning.

Funktionen returnerar `0` när den lyckas, eller `-1` vid ett fel, så det är enklast att låta returvärdet för den exporterade C-funktionen vara samma som returvärdet för anpassningsfunktionen. För att hålla det enkelt, kan koden för insticksprogrammet helt enkelt skicka vidare argumenten som ges till den exporterade C-funktionen till anpassningsfunktionen. Observera dock att inArrays måste ha följande struktur:

- `inArrays[0]` måste innehålla fältet med x-koordinater för datapunkterna

- `inArrays[1]` måste innehålla fältet med y-koordinater för datapunkterna

- `inArrays[2]` finns bara om `kstfit_linear_weighted` anropas, och måste innehålla fältet med viktningar för anpassningen.

Det enklaste sättet att försäkra att inArrays är riktigt strukturerad är att ange rätt ordning för indatavektorer i insticksprogrammets XML-fil.

Efter funktionen `kstfit_linear_unweighted` eller `kstfit_linear_weighted` anropas, har `outArrays` och `outScalars` tilldelats följande:

- `outArrays[0]` kommer att innehålla fältet med anpassade y-värden.

- `outArrays[1]` kommer att innehålla fältet med rester.

- `outArrays[2]` kommer att innehålla fältet med uppskattade parametrar för bästa anpassning.

- `outArrays[3]` kommer att innehålla kovariansmatrisen, returnerad rad för rad i fältet.

- `outScalars[0]` kommer att innehålla χ²/ν, där χ² är viktad summa av resternas kvadrater, och ν är frihetsgraden.

`outArrayLens` kommer att har rätt värde för att ange längden för varje utdatafält.

Försäkra dig om att angiven utdata i XML-filen passar ihop med den som de exporterade C-funktionerna returnerar (vilket i de flesta fall helt enkelt är utdata från anpassningsfunktionen).

### Exempel

Det följande är ett exempel på ett icke-linjärt anpassningsinsticksprogram som utför en anpassning till en exponentiell modell.

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

## Skapa insticksprogram för passfilter

&kst; tillhandahåller deklarationsfiler för att förenkla implementeringen av insticksprogram med passfilter. Användningen av deklarationsfilerna beskrivs nedan.

### Deklarationsfiler

Deklarationsfilen för passfiltret finns i `kst/plugins/pass_filters` i källkodsarkivet för &kst;. Filen heter `filters.h`. För att använda filen, inkludera den i ditt insticksprograms källkod:

    #include <../filters.h>

(som konvention placerar vi källkoden för insticksprogrammet en katalognivå under platsen där deklarationsfilerna finns).

### Nödvändiga funktioner

Deklarationsfilen `filters.h` innehåller en enda funktion som beräknar Fouriertransformen för en tillhandahållen funktion, tillämpar det tillhandahållna filtret på Fouriertransformen och beräknar därefter den inversa Fouriertransformen för den filtrerade transformen. För att tillhandahålla filtret, måste följande funktion implementeras i ditt insticksprograms källkod:

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

Funktionen ska beräkna filtrerad amplitud för frekvensen `dFreqValue`. `inScalars[]` innehåller de oförändrade indataskalärerna för insticksprogrammet, som anges i XML-filen. Troligen innehåller `inScalars[]` skärningsfrekvenser eller andra egenskaper för filtret. För att till exempel implementera ett Butterworth högpassfilter, skulle funktionen `filter_calculate` kunna implementeras som följer:

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

### Anropa filterfunktionen

Så fort den nödvändiga `filter_calculate` har implementerats, kan filterfunktionen från deklarationsfilen anropas:

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

Argumenten som skickas till den exporterade C-funktionen kan oftast skickas till `kst_pass_filter` utan ändringar. Det finns dock några bekränsningar för argumenten:

- `inArrays[0]` måste innehålla fältet med data att filtrera.

- `inScalars` ska innehålla filterspecifika parametrar som ska användas av funktionen `filter_calculate`.

Efter funktionsanropet kommer `outArrays[0]` att innehålla det filtrerade datafältet, och `outArrayLens` kommer att vara lämpligt tilldelat. Funktionen `kst_pass_filter` använder inte `outScalars`.

### Exempel

Det följande är ett exempel på ett insticksprogram med ett passfilter som implementerar Butterworth högpassfiltrering.

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