forked from bemasher/rtlamr
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathdecode.go
335 lines (271 loc) · 10.3 KB
/
decode.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
// 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 decode
import (
"bytes"
"fmt"
"log"
"math"
)
// PacketConfig specifies packet-specific radio configuration.
type PacketConfig struct {
DataRate int
BlockSize, BlockSize2 int
SymbolLength, SymbolLength2 int
SampleRate int
PreambleSymbols, PacketSymbols int
PreambleLength, PacketLength int
Preamble string
BufferLength int
CenterFreq uint32
}
func (cfg PacketConfig) Decimate(decimation int) PacketConfig {
cfg.BlockSize /= decimation
cfg.BlockSize2 /= decimation
cfg.SymbolLength /= decimation
cfg.SymbolLength2 /= decimation
cfg.SampleRate /= decimation
cfg.DataRate /= decimation
cfg.PreambleLength /= decimation
cfg.PacketLength /= decimation
cfg.BufferLength /= decimation
return cfg
}
func (d Decoder) Log() {
if d.Decimation != 1 {
log.Printf("BlockSize: %d|%d\n", d.Cfg.BlockSize, d.DecCfg.BlockSize)
log.Println("CenterFreq:", d.Cfg.CenterFreq)
log.Printf("SampleRate: %d|%d\n", d.Cfg.SampleRate, d.DecCfg.SampleRate)
log.Printf("DataRate: %d|%d\n", d.Cfg.DataRate, d.DecCfg.DataRate)
log.Printf("SymbolLength: %d|%d\n", d.Cfg.SymbolLength, d.DecCfg.SymbolLength)
log.Println("PreambleSymbols:", d.Cfg.PreambleSymbols)
log.Printf("PreambleLength: %d|%d\n", d.Cfg.PreambleLength, d.DecCfg.PreambleLength)
log.Println("PacketSymbols:", d.Cfg.PacketSymbols)
log.Printf("PacketLength: %d|%d\n", d.Cfg.PacketLength, d.DecCfg.PacketLength)
log.Println("Preamble:", d.Cfg.Preamble)
if d.Cfg.SymbolLength%d.Decimation != 0 {
log.Println("Warning: decimated symbol length is non-integral, sensitivity may be poor")
}
if d.DecCfg.SymbolLength < 3 {
log.Fatal("Error: illegal decimation factor, choose a smaller factor")
}
return
}
log.Println("CenterFreq:", d.Cfg.CenterFreq)
log.Println("SampleRate:", d.Cfg.SampleRate)
log.Println("DataRate:", d.Cfg.DataRate)
log.Println("SymbolLength:", d.Cfg.SymbolLength)
log.Println("PreambleSymbols:", d.Cfg.PreambleSymbols)
log.Println("PreambleLength:", d.Cfg.PreambleLength)
log.Println("PacketSymbols:", d.Cfg.PacketSymbols)
log.Println("PacketLength:", d.Cfg.PacketLength)
log.Println("Preamble:", d.Cfg.Preamble)
}
// Decoder contains buffers and radio configuration.
type Decoder struct {
Cfg PacketConfig
Decimation int
DecCfg PacketConfig
IQ []byte
Signal []float64
Filtered []float64
Quantized []byte
csum []float64
demod Demodulator
preamble []byte
slices [][]byte
pkt []byte
}
// Create a new decoder with the given packet configuration.
func NewDecoder(cfg PacketConfig, decimation int) (d Decoder) {
d.Cfg = cfg
d.Cfg.SymbolLength2 = d.Cfg.SymbolLength << 1
d.Cfg.SampleRate = d.Cfg.DataRate * d.Cfg.SymbolLength
d.Cfg.PreambleLength = d.Cfg.PreambleSymbols * d.Cfg.SymbolLength2
d.Cfg.PacketLength = d.Cfg.PacketSymbols * d.Cfg.SymbolLength2
d.Cfg.BlockSize = NextPowerOf2(d.Cfg.PreambleLength)
d.Cfg.BlockSize2 = d.Cfg.BlockSize << 1
d.Cfg.BufferLength = d.Cfg.PacketLength + d.Cfg.BlockSize
d.Decimation = decimation
d.DecCfg = d.Cfg.Decimate(d.Decimation)
// Allocate necessary buffers.
d.IQ = make([]byte, d.Cfg.BufferLength<<1)
d.Signal = make([]float64, d.DecCfg.BufferLength)
d.Filtered = make([]float64, d.DecCfg.BufferLength)
d.Quantized = make([]byte, d.DecCfg.BufferLength)
d.csum = make([]float64, (d.DecCfg.PacketLength - d.DecCfg.SymbolLength2 + 1))
// Calculate magnitude lookup table specified by -fastmag flag.
d.demod = NewSqrtMagLUT()
// Pre-calculate a byte-slice version of the preamble for searching.
d.preamble = make([]byte, len(d.Cfg.Preamble))
for idx := range d.Cfg.Preamble {
if d.Cfg.Preamble[idx] == '1' {
d.preamble[idx] = 1
}
}
// Slice quantized sample buffer to make searching for the preamble more
// memory local. Pre-allocate a flat buffer so memory is contiguous and
// assign slices to the buffer.
d.slices = make([][]byte, d.DecCfg.SymbolLength2)
flat := make([]byte, d.DecCfg.BlockSize2-(d.DecCfg.BlockSize2%d.DecCfg.SymbolLength2))
symbolsPerBlock := d.DecCfg.BlockSize2 / d.DecCfg.SymbolLength2
for symbolOffset := range d.slices {
lower := symbolOffset * symbolsPerBlock
upper := (symbolOffset + 1) * symbolsPerBlock
d.slices[symbolOffset] = flat[lower:upper]
}
// Signal up to the final stage is 1-bit per byte. Allocate a buffer to
// store packed version 8-bits per byte.
d.pkt = make([]byte, (d.DecCfg.PacketSymbols+7)>>3)
return
}
// Decode accepts a sample block and performs various DSP techniques to extract a packet.
func (d Decoder) Decode(input []byte) []int {
// Shift buffers to append new block.
copy(d.IQ, d.IQ[d.Cfg.BlockSize<<1:])
copy(d.Signal, d.Signal[d.DecCfg.BlockSize:])
copy(d.Filtered, d.Filtered[d.DecCfg.BlockSize:])
copy(d.Quantized, d.Quantized[d.DecCfg.BlockSize:])
copy(d.IQ[d.Cfg.PacketLength<<1:], input[:])
iqBlock := d.IQ[d.Cfg.PacketLength<<1:]
signalBlock := d.Signal[d.DecCfg.PacketLength:]
// Compute the magnitude of the new block.
d.demod.Execute(iqBlock, signalBlock)
signalBlock = d.Signal[d.DecCfg.PacketLength-d.DecCfg.SymbolLength2:]
filterBlock := d.Filtered[d.DecCfg.PacketLength-d.DecCfg.SymbolLength2:]
// Perform matched filter on new block.
d.Filter(signalBlock, filterBlock)
// Perform bit-decision on new block.
Quantize(filterBlock, d.Quantized[d.DecCfg.PacketLength-d.DecCfg.SymbolLength2:])
// Pack the quantized signal into slices for searching.
d.Pack(d.Quantized[:d.DecCfg.BlockSize2], d.slices)
// Return a list of indexes the preamble exists at.
return d.Search(d.slices, d.preamble)
}
// A Demodulator knows how to demodulate an array of uint8 IQ samples into an
// array of float64 samples.
type Demodulator interface {
Execute([]byte, []float64)
}
// Default Magnitude Lookup Table
type MagLUT []float64
// Pre-computes normalized squares with most common DC offset for rtl-sdr dongles.
func NewSqrtMagLUT() (lut MagLUT) {
lut = make([]float64, 0x100)
for idx := range lut {
lut[idx] = 127.4 - float64(idx)
lut[idx] *= lut[idx]
}
return
}
// Calculates complex magnitude on given IQ stream writing result to output.
func (lut MagLUT) Execute(input []byte, output []float64) {
decIdx := 0
dec := (len(input) / len(output))
for idx := 0; decIdx < len(output); idx += dec {
output[decIdx] = math.Sqrt(lut[input[idx]] + lut[input[idx+1]])
decIdx++
}
}
// Matched filter for Manchester coded signals. Output signal's sign at each
// sample determines the bit-value since Manchester symbols have odd symmetry.
func (d Decoder) Filter(input, output []float64) {
// Computing the cumulative summation over the signal simplifies
// filtering to the difference of a pair of subtractions.
var sum float64
for idx, v := range input {
sum += v
d.csum[idx+1] = sum
}
// Filter result is difference of summation of lower and upper symbols.
lower := d.csum[d.DecCfg.SymbolLength:]
upper := d.csum[d.DecCfg.SymbolLength2:]
n := len(input) - d.DecCfg.SymbolLength2
for idx := 0; idx < n; idx++ {
output[idx] = (lower[idx] - d.csum[idx]) - (upper[idx] - lower[idx])
}
return
}
// Bit-value is determined by the sign of each sample after filtering.
func Quantize(input []float64, output []byte) {
for idx, val := range input {
output[idx] = byte(math.Float64bits(val)>>63) ^ 0x01
}
return
}
// Packs quantized signal into slices such that the first rank represents
// sample offsets and the second represents the value of each symbol from the
// given offset.
//
// Transforms:
// <--Sym1--><--Sym2--><--Sym3--><--Sym4--><--Sym5--><--Sym6--><--Sym7--><--Sym8-->
// <12345678><12345678><12345678><12345678><12345678><12345678><12345678><12345678>
// to:
// <11111111><22222222><33333333><44444444><55555555><66666666><77777777><88888888>
func (d Decoder) Pack(input []byte, slices [][]byte) {
for symbolOffset, slice := range slices {
for symbolIdx := range slice {
slice[symbolIdx] = input[symbolIdx*d.DecCfg.SymbolLength2+symbolOffset]
}
}
return
}
// For each sample offset look for the preamble. Return a list of indexes the
// preamble is found at. Indexes are absolute in the unsliced quantized
// buffer.
func (d Decoder) Search(slices [][]byte, preamble []byte) (indexes []int) {
preambleLength := len(preamble)
for symbolOffset, slice := range slices {
for symbolIdx := range slice[:len(slice)-preambleLength] {
if bytes.Equal(preamble, slice[symbolIdx:][:preambleLength]) {
indexes = append(indexes, symbolIdx*d.DecCfg.SymbolLength2+symbolOffset)
}
}
}
return
}
// Given a list of indeces the preamble exists at, sample the appropriate bits
// of the signal's bit-decision. Pack bits of each index into an array of byte
// arrays and return.
func (d Decoder) Slice(indices []int) (pkts [][]byte) {
// We will likely find multiple instances of the message so only keep
// track of unique instances.
seen := make(map[string]bool)
// For each of the indices the preamble exists at.
for _, qIdx := range indices {
// Check that we're still within the first sample block. We'll catch
// the message on the next sample block otherwise.
if qIdx > d.DecCfg.BlockSize {
continue
}
// Packet is 1 bit per byte, pack to 8-bits per byte.
for pIdx := 0; pIdx < d.DecCfg.PacketSymbols; pIdx++ {
d.pkt[pIdx>>3] <<= 1
d.pkt[pIdx>>3] |= d.Quantized[qIdx+(pIdx*d.DecCfg.SymbolLength2)]
}
// Store the packet in the seen map and append to the packet list.
pktStr := fmt.Sprintf("%02X", d.pkt)
if !seen[pktStr] {
seen[pktStr] = true
pkts = append(pkts, make([]byte, len(d.pkt)))
copy(pkts[len(pkts)-1], d.pkt)
}
}
return
}
func NextPowerOf2(v int) int {
return 1 << uint(math.Ceil(math.Log2(float64(v))))
}