# HG changeset patch # User Mikael Berthe # Date 1625943254 -7200 # Node ID 3f39d3cd68cecdbaab655e7b97be979324b9ce15 # Parent e882f78c302eaf7303bcfa6c0ee6436232dff6f7 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. diff -r e882f78c302e -r 3f39d3cd68ce 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] }