From 233608205f074c92fdb558cef03ba02e7c1d0409 Mon Sep 17 00:00:00 2001 From: Eric Sandeen Date: Sun, 15 Mar 2015 10:19:52 -0500 Subject: [PATCH] r900: decode leak, fraud, and backflow status flags TL;DR - this patch decodes the unknown2 and unknown4 fields into leak, fraud, and backflow status information. Pretty output now shows: NoUse:$BIN BackFlow:$FLAG ... Leak:$BIN LeakNow:$FLAG Where BackFlow is 0, 1, or 2 for no, low, or high backflow in past 35d; LeakNow is 0, 1, or 2 for no, low, or high leak in the past 24h; NoUse and Leak is a number from 0 through 6 representing the number of days in the past 35 days for which either no use or a leak has occurred, according to this table: bucket days ------ ---- 0 0 1 1-2 2 3-7 3 8-14 4 15-21 5 22-34 6 35+ I sorted this out using public documentation such as https://www.neptunetg.com/About/Case-Studies/Water/Westbank-Irrigation-District,-BC which indicates that the utility can gather stats on days of no use and days of leak in the past 35 days, and that it is divided into buckets as shown in the table above. Using this public information, intentionally triggering the leak flag in my home, and listening to many meters over time led me to this decoding of the unknown2 and unknown4 fields. The 8 unknown2 bits seem to show days of no use and backflow information, like this (? is still unknown, but this patch lumps it in with the bucket number): ???BBBHL BBB: bucket # for days of no use in past 35 days H: high backflow in past 35 days L: low backflow in past 35 days Backflow seems to require some minimum frequency of occurrence before it will set the flag. Similarly, the 6 unknown4 bits look like this: ?BBBHL BBB: bucket # for days of leak in past 35 days H: continuous leak in past 24 hours L: intermittent leak in past 24 hours Intermittent leaks are described by Neptune in other public docs as: "Intermittent leak indicates that water has been used for at least 50 of the 96 15-minute intervals during a 24-hour period" and a continuous leak indicates use in every 15-minute period over the past 24h. --- r900/r900.go | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/r900/r900.go b/r900/r900.go index 326c59ab6..8aa5c4c57 100644 --- a/r900/r900.go +++ b/r900/r900.go @@ -220,19 +220,23 @@ func (p Parser) Parse(indices []int) (msgs []parse.Message) { id, _ := strconv.ParseUint(bits[:32], 2, 32) unkn1, _ := strconv.ParseUint(bits[32:40], 2, 8) - unkn2, _ := strconv.ParseUint(bits[40:48], 2, 8) + nouse, _ := strconv.ParseUint(bits[40:46], 2, 6) + backflow, _ := strconv.ParseUint(bits[46:48], 2, 2) consumption, _ := strconv.ParseUint(bits[48:72], 2, 24) unkn3, _ := strconv.ParseUint(bits[72:74], 2, 2) - unkn4, _ := strconv.ParseUint(bits[74:80], 2, 6) + leak, _ := strconv.ParseUint(bits[74:78], 2, 4) + leaknow, _ := strconv.ParseUint(bits[78:80], 2, 2) var r900 R900 r900.ID = uint32(id) r900.Unkn1 = uint8(unkn1) - r900.Unkn2 = uint8(unkn2) + r900.NoUse = uint8(nouse) + r900.BackFlow = uint8(backflow) r900.Consumption = uint32(consumption) r900.Unkn3 = uint8(unkn3) - r900.Unkn4 = uint8(unkn4) + r900.Leak = uint8(leak) + r900.LeakNow = uint8(leaknow) msgs = append(msgs, r900) } @@ -243,10 +247,13 @@ func (p Parser) Parse(indices []int) (msgs []parse.Message) { type R900 struct { ID uint32 `xml:",attr"` // 32 bits Unkn1 uint8 `xml:",attr"` // 8 bits - Unkn2 uint8 `xml:",attr"` // 8 bits + NoUse uint8 `xml:",attr"` // 6 bits, day bins of no use + BackFlow uint8 `xml:",attr"` // 2 bits, backflow past 35d hi/lo Consumption uint32 `xml:",attr"` // 24 bits Unkn3 uint8 `xml:",attr"` // 2 bits - Unkn4 uint8 `xml:",attr"` // 6 bits + Leak uint8 `xml:",attr"` // 4 bits, day bins of leak + LeakNow uint8 `xml:",attr"` // 2 bits, leak past 24h hi/lo + } func (r900 R900) MsgType() string { @@ -262,23 +269,27 @@ func (r900 R900) MeterType() uint8 { } func (r900 R900) String() string { - return fmt.Sprintf("{ID:%10d Unkn1:0x%02X Unkn2:0x%02X Consumption:%8d Unkn3:0x%02X Unkn4:0x%02X}", + return fmt.Sprintf("{ID:%10d Unkn1:0x%02X NoUse:%2d BackFlow:%1d Consumption:%8d Unkn3:0x%02X Leak:%2d LeakNow:%1d}", r900.ID, r900.Unkn1, - r900.Unkn2, + r900.NoUse, + r900.BackFlow, r900.Consumption, r900.Unkn3, - r900.Unkn4, + r900.Leak, + r900.LeakNow, ) } func (r900 R900) Record() (r []string) { r = append(r, strconv.FormatUint(uint64(r900.ID), 10)) r = append(r, strconv.FormatUint(uint64(r900.Unkn1), 10)) - r = append(r, strconv.FormatUint(uint64(r900.Unkn2), 10)) + r = append(r, strconv.FormatUint(uint64(r900.NoUse), 10)) + r = append(r, strconv.FormatUint(uint64(r900.BackFlow), 10)) r = append(r, strconv.FormatUint(uint64(r900.Consumption), 10)) r = append(r, strconv.FormatUint(uint64(r900.Unkn3), 10)) - r = append(r, strconv.FormatUint(uint64(r900.Unkn4), 10)) + r = append(r, strconv.FormatUint(uint64(r900.Leak), 10)) + r = append(r, strconv.FormatUint(uint64(r900.LeakNow), 10)) return }