Add a --reduce flag
authorMikael Berthe <mikael@lilotux.net>
Sat, 10 Jul 2021 20:54:14 +0200
changeset 28 3f39d3cd68ce
parent 27 e882f78c302e
child 29 2b8506d91ad5
Add a --reduce flag The reduce flag regroups close measurements (that is, within 1 hour from the next one, threshold currently hardcoded) and replaces them with their average.
gobm65.go
--- a/gobm65.go	Sat Mar 13 19:31:49 2021 +0100
+++ b/gobm65.go	Sat Jul 10 20:54:14 2021 +0200
@@ -25,6 +25,9 @@
 // ... also display World Health Organization classification:
 // % gobm65 --stats --class
 //
+// Display all records but reduce the list so that close records are replaced
+// with their average:
+// % gobm65 --reduce
 // Display the latest 3 records with the average:
 // % gobm65 -l 3 --average
 // Display all records since a specific date:
@@ -346,6 +349,13 @@
 	return
 }
 
+// diffTime returns the duration between two (decreasing) sorted measurements
+func diffTime(a, b measurement) time.Duration {
+	dateA := time.Date(a.Year, time.Month(a.Month), a.Day, a.Hour, a.Minute, 0, 0, time.Local)
+	dateB := time.Date(b.Year, time.Month(b.Month), b.Day, b.Hour, b.Minute, 0, 0, time.Local)
+	return dateA.Sub(dateB)
+}
+
 func average(items []measurement) (measurement, error) {
 	var avgMeasure measurement
 	var avgCount int
@@ -459,6 +469,49 @@
 	return dev, nil
 }
 
+// reduceList reduces the measurement list by regrouping close measurements;
+// if the time difference between several measurements is less than the given
+// threshold, they are replaced with their average.
+func reduceList(list []measurement, threshold time.Duration) []measurement {
+	var newList, acc []measurement
+
+	// flushAcc flushes the accumulator and adds an average measurement to
+	// the new list.
+	flushAcc := func() {
+		// setAvgTime computes the average date of the list and sets
+		// the measurement date to this average.
+		setAvgTime := func(m *measurement, l []measurement) {
+			var timestamp int64 = 0
+			for _, i := range l {
+				iDate := time.Date(i.Year, time.Month(i.Month), i.Day,
+					i.Hour, i.Minute, 0, 0, time.Local)
+				timestamp += iDate.Unix()
+			}
+			timestamp /= int64(len(l))
+			avgTime := time.Unix(timestamp, 0)
+			m.Year = avgTime.Year()
+			m.Month = int(avgTime.Month())
+			m.Day = avgTime.Day()
+			m.Hour = avgTime.Hour()
+			m.Minute = avgTime.Minute()
+		}
+		avg, _ := average(acc)
+		avg.Header = acc[0].Header // Arbitrary…
+		setAvgTime(&avg, acc)
+		newList = append(newList, avg)
+		acc = acc[:0]
+	}
+
+	for _, item := range list {
+		if len(acc) > 0 && diffTime(acc[len(acc)-1], item) > threshold {
+			flushAcc()
+		}
+		acc = append(acc, item)
+	}
+	flushAcc()
+	return newList
+}
+
 func (m measurement) WHOClass() (int, int) {
 	flag := 0
 
@@ -527,6 +580,7 @@
 	device := flag.StringP("device", "d", "/dev/ttyUSB0", "Serial device")
 	fromTime := flag.String("from-time", "", "Select records after time (HH:MM)")
 	toTime := flag.String("to-time", "", "Select records bofore time (HH:MM)")
+	reduce := flag.BoolP("reduce", "r", false, "Reduce number of measurements (regroup measurements)")
 
 	flag.StringVar(fromDate, "since", "", "Same as --from-date")
 
@@ -663,6 +717,10 @@
 		items = newItems
 	}
 
+	if *reduce {
+		items = reduceList(items, time.Hour)
+	}
+
 	if *limit > 0 && len(items) > int(*limit) {
 		items = items[0:*limit]
 	}