github.com/go-graphite/carbonapi@v0.17.0/expr/functions/fft/function.go (about)

     1  package fft
     2  
     3  import (
     4  	"context"
     5  	"math/cmplx"
     6  
     7  	"github.com/go-graphite/carbonapi/expr/helper"
     8  	"github.com/go-graphite/carbonapi/expr/interfaces"
     9  	"github.com/go-graphite/carbonapi/expr/types"
    10  	"github.com/go-graphite/carbonapi/pkg/parser"
    11  	realFFT "github.com/mjibson/go-dsp/fft"
    12  )
    13  
    14  type fft struct{}
    15  
    16  func GetOrder() interfaces.Order {
    17  	return interfaces.Any
    18  }
    19  
    20  func New(configFile string) []interfaces.FunctionMetadata {
    21  	res := make([]interfaces.FunctionMetadata, 0)
    22  	f := &fft{}
    23  	functions := []string{"fft"}
    24  	for _, n := range functions {
    25  		res = append(res, interfaces.FunctionMetadata{Name: n, F: f})
    26  	}
    27  	return res
    28  }
    29  
    30  func extractComponent(m *types.MetricData, values []complex128, t string, f func(x complex128) float64) *types.MetricData {
    31  	r := m.CopyTag("fft("+m.Name+","+t+")", m.Tags)
    32  	r.Values = make([]float64, len(values))
    33  	for i, v := range values {
    34  		r.Values[i] = f(v)
    35  	}
    36  	return r
    37  }
    38  
    39  // fft(seriesList, mode)
    40  // mode: "", abs, phase. Empty string means "both"
    41  func (f *fft) Do(ctx context.Context, eval interfaces.Evaluator, e parser.Expr, from, until int64, values map[parser.MetricRequest][]*types.MetricData) ([]*types.MetricData, error) {
    42  	arg, err := helper.GetSeriesArg(ctx, eval, e.Arg(0), from, until, values)
    43  	if err != nil {
    44  		return nil, err
    45  	}
    46  
    47  	mode, _ := e.GetStringArg(1)
    48  
    49  	var results []*types.MetricData
    50  	if mode == "abs" || mode == "phase" {
    51  		results = make([]*types.MetricData, 0, len(arg))
    52  	} else {
    53  		results = make([]*types.MetricData, 0, 2*len(arg))
    54  	}
    55  	for _, a := range arg {
    56  		values := realFFT.FFTReal(a.Values)
    57  
    58  		switch mode {
    59  		case "", "both", "all":
    60  			results = append(results, extractComponent(a, values, "abs", cmplx.Abs), extractComponent(a, values, "phase", cmplx.Phase))
    61  		case "abs":
    62  			results = append(results, extractComponent(a, values, "abs", cmplx.Abs))
    63  		case "phase":
    64  			results = append(results, extractComponent(a, values, "phase", cmplx.Phase))
    65  		}
    66  	}
    67  	return results, nil
    68  }
    69  
    70  // Description is auto-generated description, based on output of https://github.com/graphite-project/graphite-web
    71  func (f *fft) Description() map[string]types.FunctionDescription {
    72  	return map[string]types.FunctionDescription{
    73  		"fft": {
    74  			Description: "An algorithm that samples a signal over a period of time (or space) and divides it into its frequency components. Computes discrete Fourier transform https://en.wikipedia.org/wiki/Fast_Fourier_transform \n\nExample:\n\n.. code-block:: none\n\n  &target=fft(server*.requests_per_second)\n\n  &target=fft(server*.requests_per_second, \"abs\")\n",
    75  			Function:    "fft(seriesList, mode)",
    76  			Group:       "Transform",
    77  			Module:      "graphite.render.functions.custom",
    78  			Name:        "fft",
    79  			Params: []types.FunctionParam{
    80  				{
    81  					Name:     "seriesList",
    82  					Required: true,
    83  					Type:     types.SeriesList,
    84  				},
    85  				{
    86  					Name:     "mode",
    87  					Required: false,
    88  					Type:     types.String,
    89  					Options: types.StringsToSuggestionList([]string{
    90  						"abs",
    91  						"phase",
    92  						"both",
    93  					}),
    94  				},
    95  			},
    96  			SeriesChange: true, // function aggregate metrics or change series items count
    97  			NameChange:   true, // name changed
    98  			ValuesChange: true, // values changed
    99  		},
   100  	}
   101  }