2022-01-05 07:52:29 +03:00
|
|
|
/*
|
|
|
|
DESCRIPTION
|
2022-01-06 06:25:40 +03:00
|
|
|
Plotting functions for the turbidity sensor results.
|
2022-01-05 07:52:29 +03:00
|
|
|
|
|
|
|
AUTHORS
|
2022-01-06 06:25:40 +03:00
|
|
|
Russell Stanley <russell@ausocean.org>
|
2022-01-05 07:52:29 +03:00
|
|
|
|
|
|
|
LICENSE
|
|
|
|
Copyright (C) 2020 the Australian Ocean Lab (AusOcean)
|
|
|
|
|
|
|
|
It is free software: you can redistribute it and/or modify them
|
|
|
|
under the terms of the GNU General Public License as published by the
|
|
|
|
Free Software Foundation, either version 3 of the License, or (at your
|
|
|
|
option) any later version.
|
|
|
|
|
|
|
|
It is distributed in the hope that it will be useful, but WITHOUT
|
|
|
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
|
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
|
|
for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
in gpl.txt. If not, see http://www.gnu.org/licenses.
|
|
|
|
*/
|
|
|
|
|
2021-12-17 09:03:31 +03:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2021-12-21 08:03:21 +03:00
|
|
|
"math"
|
2021-12-17 09:03:31 +03:00
|
|
|
|
|
|
|
"gonum.org/v1/plot"
|
|
|
|
"gonum.org/v1/plot/plotter"
|
|
|
|
"gonum.org/v1/plot/plotutil"
|
|
|
|
"gonum.org/v1/plot/vg"
|
2021-12-21 08:03:21 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
// Normalize values in a slice between 0 and 1.
|
|
|
|
func normalize(slice []float64) []float64 {
|
|
|
|
max := -math.MaxFloat64
|
|
|
|
min := math.MaxFloat64
|
2021-12-21 04:43:05 +03:00
|
|
|
out := make([]float64, len(slice))
|
2021-12-17 09:03:31 +03:00
|
|
|
|
2021-12-21 04:43:05 +03:00
|
|
|
if len(slice) <= 1 {
|
2021-12-17 09:03:31 +03:00
|
|
|
return slice
|
|
|
|
}
|
|
|
|
|
2022-01-06 06:25:40 +03:00
|
|
|
// Find the max and min values of the slice.
|
2021-12-21 04:43:05 +03:00
|
|
|
for i := range slice {
|
2021-12-17 09:03:31 +03:00
|
|
|
if slice[i] > max {
|
|
|
|
max = slice[i]
|
|
|
|
}
|
|
|
|
if slice[i] < min {
|
|
|
|
min = slice[i]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-21 04:43:05 +03:00
|
|
|
for i := range slice {
|
2021-12-17 09:03:31 +03:00
|
|
|
out[i] = (slice[i] - min) / (max - min)
|
|
|
|
}
|
|
|
|
return out
|
|
|
|
}
|
|
|
|
|
2021-12-21 08:03:21 +03:00
|
|
|
// Return the average of a slice.
|
|
|
|
func average(slice []float64) float64 {
|
2022-01-06 06:25:40 +03:00
|
|
|
var out float64
|
2021-12-21 04:43:05 +03:00
|
|
|
|
2022-01-06 06:25:40 +03:00
|
|
|
// Sum all elements in the slice.
|
2021-12-21 04:43:05 +03:00
|
|
|
for i := range slice {
|
|
|
|
out += slice[i]
|
|
|
|
}
|
|
|
|
return out / float64(len(slice))
|
|
|
|
}
|
2021-12-17 09:03:31 +03:00
|
|
|
|
2021-12-21 08:03:21 +03:00
|
|
|
func plotResults(x, saturation, contrast []float64) error {
|
2021-12-17 09:03:31 +03:00
|
|
|
err := plotToFile(
|
|
|
|
"Results",
|
2022-01-05 03:49:31 +03:00
|
|
|
"Almond Milk (ml)",
|
2021-12-17 09:03:31 +03:00
|
|
|
"Score",
|
|
|
|
func(p *plot.Plot) error {
|
|
|
|
return plotutil.AddLinePoints(p,
|
2022-01-05 03:49:31 +03:00
|
|
|
"Contrast", plotterXY(x, contrast),
|
|
|
|
"Saturation", plotterXY(x, saturation),
|
2021-12-17 09:03:31 +03:00
|
|
|
)
|
|
|
|
},
|
|
|
|
)
|
|
|
|
if err != nil {
|
2021-12-21 08:03:21 +03:00
|
|
|
return fmt.Errorf("Could not plot results: %w", err)
|
2021-12-17 09:03:31 +03:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// plotToFile creates a plot with a specified name and x&y titles using the
|
|
|
|
// provided draw function, and then saves to a PNG file with filename of name.
|
|
|
|
func plotToFile(name, xTitle, yTitle string, draw func(*plot.Plot) error) error {
|
|
|
|
p := plot.New()
|
|
|
|
p.Title.Text = name
|
|
|
|
p.X.Label.Text = xTitle
|
|
|
|
p.Y.Label.Text = yTitle
|
|
|
|
err := draw(p)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("could not draw plot contents: %w", err)
|
|
|
|
}
|
|
|
|
if err := p.Save(15*vg.Centimeter, 15*vg.Centimeter, "plots/"+name+".png"); err != nil {
|
|
|
|
return fmt.Errorf("could not save plot: %w", err)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// plotterXY provides a plotter.XYs type value based on the given x and y data.
|
|
|
|
func plotterXY(x, y []float64) plotter.XYs {
|
|
|
|
xy := make(plotter.XYs, len(x))
|
|
|
|
for i := range x {
|
|
|
|
xy[i].X = x[i]
|
|
|
|
xy[i].Y = y[i]
|
|
|
|
}
|
|
|
|
return xy
|
|
|
|
}
|