rtlamr/scm/scm.go
bemasher 42ceb2cce7 Remove FastMag
Simulation shows that FastMag is of roughly equivalent sensitivity to
MagLUT while not providing much better performance.
2015-07-11 01:36:07 -06:00

155 lines
3.9 KiB
Go

// RTLAMR - An rtl-sdr receiver for smart meters operating in the 900MHz ISM band.
// Copyright (C) 2015 Douglas Hall
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package scm
import (
"encoding/binary"
"fmt"
"strconv"
"github.com/bemasher/rtlamr/crc"
"github.com/bemasher/rtlamr/decode"
"github.com/bemasher/rtlamr/parse"
)
func NewPacketConfig(symbolLength int) (cfg decode.PacketConfig) {
cfg.CenterFreq = 912600155
cfg.DataRate = 32768
cfg.SymbolLength = symbolLength
cfg.PreambleSymbols = 21
cfg.PacketSymbols = 96
cfg.Preamble = "111110010101001100000"
return
}
type Parser struct {
decode.Decoder
crc.CRC
}
func NewParser(symbolLength, decimation int) (p Parser) {
p.Decoder = decode.NewDecoder(NewPacketConfig(symbolLength), decimation)
p.CRC = crc.NewCRC("BCH", 0, 0x6F63, 0)
return
}
func (p Parser) Dec() decode.Decoder {
return p.Decoder
}
func (p Parser) Cfg() decode.PacketConfig {
return p.Decoder.Cfg
}
func (p Parser) Parse(indices []int) (msgs []parse.Message) {
seen := make(map[string]bool)
for _, pkt := range p.Decoder.Slice(indices) {
s := string(pkt)
if seen[s] {
continue
}
seen[s] = true
data := parse.NewDataFromBytes(pkt)
// If the packet is too short, bail.
if l := len(data.Bytes); l != 12 {
continue
}
// If the checksum fails, bail.
if p.Checksum(data.Bytes[2:12]) != 0 {
continue
}
scm := NewSCM(data)
// If the meter id is 0, bail.
if scm.ID == 0 {
continue
}
msgs = append(msgs, scm)
}
return
}
// Standard Consumption Message
type SCM struct {
ID uint32 `xml:",attr"`
Type uint8 `xml:",attr"`
TamperPhy uint8 `xml:",attr"`
TamperEnc uint8 `xml:",attr"`
Consumption uint32 `xml:",attr"`
ChecksumVal uint16 `xml:"Checksum,attr"`
}
func NewSCM(data parse.Data) (scm SCM) {
ertid, _ := strconv.ParseUint(data.Bits[21:23]+data.Bits[56:80], 2, 26)
erttype, _ := strconv.ParseUint(data.Bits[26:30], 2, 4)
tamperphy, _ := strconv.ParseUint(data.Bits[24:26], 2, 2)
tamperenc, _ := strconv.ParseUint(data.Bits[30:32], 2, 2)
consumption, _ := strconv.ParseUint(data.Bits[32:56], 2, 24)
checksum, _ := strconv.ParseUint(data.Bits[80:96], 2, 16)
scm.ID = uint32(ertid)
scm.Type = uint8(erttype)
scm.TamperPhy = uint8(tamperphy)
scm.TamperEnc = uint8(tamperenc)
scm.Consumption = uint32(consumption)
scm.ChecksumVal = uint16(checksum)
return
}
func (scm SCM) MsgType() string {
return "SCM"
}
func (scm SCM) MeterID() uint32 {
return scm.ID
}
func (scm SCM) MeterType() uint8 {
return scm.Type
}
func (scm SCM) Checksum() []byte {
checksum := make([]byte, 2)
binary.BigEndian.PutUint16(checksum, scm.ChecksumVal)
return checksum
}
func (scm SCM) String() string {
return fmt.Sprintf("{ID:%8d Type:%2d Tamper:{Phy:%02X Enc:%02X} Consumption:%8d CRC:0x%04X}",
scm.ID, scm.Type, scm.TamperPhy, scm.TamperEnc, scm.Consumption, scm.ChecksumVal,
)
}
func (scm SCM) Record() (r []string) {
r = append(r, strconv.FormatUint(uint64(scm.ID), 10))
r = append(r, strconv.FormatUint(uint64(scm.Type), 10))
r = append(r, "0x"+strconv.FormatUint(uint64(scm.TamperPhy), 16))
r = append(r, "0x"+strconv.FormatUint(uint64(scm.TamperEnc), 16))
r = append(r, strconv.FormatUint(uint64(scm.Consumption), 10))
r = append(r, "0x"+strconv.FormatUint(uint64(scm.ChecksumVal), 16))
return
}