The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
grafana/pkg/tsdb/grafana-testdata-datasource/sims/waveform.go

115 lines
3.0 KiB

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
}