grafana/pkg/tsdb/grafana-testdata-datasource/sims/waveform.go

116 lines
3.0 KiB
Go

package sims
import (
"math"
"math/rand"
"time"
"github.com/grafana/grafana-plugin-sdk-go/data"
)
type waveformSim struct {
key simulationKey
cfg waveformConfig
calculator func(x float64, cfg *waveformConfig) float64
}
var (
_ Simulation = (*waveformSim)(nil)
)
type waveformConfig struct {
Period float64 `json:"period"` // seconds
Offset float64 `json:"offset,omitempty"` // Y shift
Phase float64 `json:"phase,omitempty"` // X shift // +- 1 (will scale the )
Amplitude float64 `json:"amplitude"` // Y size
Noise float64 `json:"noise,omitempty"` // random noise to add
}
func (s *waveformSim) GetState() simulationState {
return simulationState{
Key: s.key,
Config: s.cfg,
}
}
func (s *waveformSim) SetConfig(vals map[string]any) error {
return updateConfigObjectFromJSON(s.cfg, vals)
}
func (s *waveformSim) NewFrame(size int) *data.Frame {
frame := data.NewFrameOfFieldTypes("", size,
data.FieldTypeTime, // time
data.FieldTypeFloat64, // value
)
frame.Fields[0].Name = data.TimeSeriesTimeFieldName
frame.Fields[1].Name = data.TimeSeriesValueFieldName
return frame
}
func (s *waveformSim) GetValues(t time.Time) map[string]any {
x := 0.0
if s.cfg.Period > 0 {
periodMS := s.cfg.Period * 1000
ms := t.UnixMilli() % int64(periodMS)
x = ((float64(ms) / periodMS) * 2 * math.Pi) // 0 >> 2Pi
}
v := s.calculator(x, &s.cfg)
noise := s.cfg.Noise
if noise > 0 {
gen := rand.New(rand.NewSource(t.UnixMilli())) // consistent for the value
v += (gen.Float64() * 2.0 * noise) - noise
}
return map[string]any{
data.TimeSeriesTimeFieldName: t,
data.TimeSeriesValueFieldName: v,
}
}
func (s *waveformSim) Close() error {
return nil
}
func newSinewaveInfo() simulationInfo {
sf := waveformConfig{
Period: 10,
Amplitude: 1,
Offset: 0,
Phase: 0,
}
df := data.NewFrame("")
df.Fields = append(df.Fields, data.NewField("period", nil, []float64{sf.Period}))
df.Fields = append(df.Fields, data.NewField("offset", nil, []float64{sf.Offset}))
df.Fields = append(df.Fields, data.NewField("phase", nil, []float64{sf.Phase}))
df.Fields = append(df.Fields, data.NewField("amplitude", nil, []float64{sf.Amplitude}))
df.Fields = append(df.Fields, data.NewField("noise", nil, []float64{sf.Noise}))
f := data.NewField("period", nil, []float64{sf.Period})
f.Config = &data.FieldConfig{Unit: "s"} // seconds
df.Fields = append(df.Fields, f)
return simulationInfo{
Type: "sine",
Name: "Sine",
Description: "Sinewave generator",
ConfigFields: df,
OnlyForward: false,
create: func(cfg simulationState) (Simulation, error) {
s := &waveformSim{
key: cfg.Key,
cfg: sf, // default value
calculator: sinewaveCalculator,
}
err := updateConfigObjectFromJSON(&s.cfg, cfg.Config) // override any fields
return s, err
},
}
}
func sinewaveCalculator(x float64, cfg *waveformConfig) float64 {
return (math.Sin(x) * cfg.Amplitude) + cfg.Offset
}