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 }