You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

252 lines
5.8 KiB
Go

package pocsag
import (
"bufio"
"fmt"
"io"
"os"
"time"
"github.com/dhogborg/go-pocsag/internal/datatypes"
"github.com/dhogborg/go-pocsag/internal/utils"
)
type StreamReader struct {
Stream *bufio.Reader
// 0 for auto
baud int
}
// NewStreamReader returns a new stream reader for the source provided.
// Set bauds 0 for automatic detection.
func NewStreamReader(source io.Reader, bauds int) *StreamReader {
return &StreamReader{
Stream: bufio.NewReader(source),
baud: bauds,
}
}
// StartScan takes a channel on which bitstreams will be written when found and parsed.
// The scanner will continue indefently or to EOF is reached
func (s *StreamReader) StartScan(bitstream chan []datatypes.Bit) {
fmt.Println("Starting transmission scanner")
for {
bytes := make([]byte, 8192)
c, err := s.Stream.Read(bytes)
if err != nil {
println(err.Error())
os.Exit(0)
}
stream := s.bToInt16(bytes[:c])
start, bitlength := s.ScanTransmissionStart(stream)
if start > 0 {
blue.Println("-- Transmission received at", time.Now(), "--------------")
transmission := s.ReadTransmission(stream[start:])
bits := utils.StreamToBits(transmission, bitlength)
if DEBUG && LEVEL > 2 {
utils.PrintBitstream(bits)
}
bitstream <- bits
}
}
}
// ReadTransmission reads the beginning and subsequent datapackages into
// a new buffer until encounters noise instead of signal.
func (s *StreamReader) ReadTransmission(beginning []int16) []int16 {
stream := make([]int16, 0)
stream = append(stream, beginning...)
for {
bytes := make([]byte, 8192)
c, _ := s.Stream.Read(bytes)
if c > 0 {
bstr := s.bToInt16(bytes[:c])
stream = append(stream, bstr...)
if s.isNoise(bstr) {
if DEBUG && LEVEL > 1 {
print("\n")
println("Transmission end (high noise level)")
}
break
}
}
}
return stream
}
// ScanTransmissionStart scans for repeated 1010101010101 pattern of bits in the
// stream. A minimum of 400 samples is required to sucessfully sync the receiver with
// the stream. ScanTransmissionStart looks for when the signal wave changes from high to low
// and reversed, and measures the distance between those changes. They should correspond to
// the bitlength determined by the current baud-rate. An attempt at guessing the baudrate is
// also made when a repeated pattern is found.
// retuned is the index of the stream on which the caller should begin reading bits, and
// the estimated bitlength, the number of samples between each bit center in transmission stream.
func (s *StreamReader) ScanTransmissionStart(stream []int16) (int, int) {
if len(stream) == 0 {
return -1, 0
}
switches := []int{}
prevsamp := stream[0]
first_switch := -1
// find the indexes where we cross the 0-boundary
// if we switch sides we store the index in an array for further analasys
for a, sample := range stream {
if (prevsamp > 0 && sample < 0) || (prevsamp < 0 && sample > 0) {
switches = append(switches, a)
if first_switch < 0 {
first_switch = a
}
}
prevsamp = sample
}
// find the mean distance between boundary corsses
sum := 0.0
for a := 0; a < len(switches)-1; a += 1 {
sum += float64(switches[a+1] - switches[a])
}
mean_bitlength := sum / float64(len(switches)-1)
bitlength := float64(s.bitlength(int(mean_bitlength)))
// if bitlength is not on a scale of known baudrates then
// we probably don't have a pocsag sync-transmission
if bitlength < 0 {
return -1, 0
}
if DEBUG {
blue.Println("Mean bitlength:", mean_bitlength)
blue.Println("Determined bitlength:", bitlength)
}
// look at every other sample to see if we have a repeating pattern with matching size
confidence := 0
for a := 0; a < len(switches)-3; a += 1 {
// length from switch a to a+1
w1 := float64(switches[a+1] - switches[a])
w2 := float64(switches[a+3] - switches[a+2])
// how much the persumed bits vary from eachother
intravariance := (w1 / w2) - 1
if intravariance < 0 {
intravariance = intravariance * -1
}
// how much the persumed bits vary from the determined bitlength
baudvariance := (w1 / bitlength) - 1
if baudvariance < 0 {
baudvariance = baudvariance * -1
}
// don't stray more than 20%
if intravariance < 0.2 && baudvariance < 0.2 {
confidence += 1
} else {
confidence = 0
}
if confidence > 10 {
if DEBUG {
blue.Println("Found bitsync")
}
return switches[a] + int(bitlength/2), int(bitlength)
}
}
return -1, 0
}
// bitlength returns the proper bitlength from a calcualated mean distance between
// wave transitions. If the baudrate is set by configuration then that is used instead.
func (s *StreamReader) bitlength(mean int) int {
if mean > 150 && mean < 170 {
return 160
} else if mean > 75 && mean < 85 || s.baud == 600 {
return 80
} else if mean > 35 && mean < 45 || s.baud == 1200 {
return 40
} else if mean > 15 && mean < 25 || s.baud == 2400 {
return 20
} else {
return -1
}
}
// isNoise detects noise by calculating the number of times the signal goes over the 0-line
// during a signal this value is between 25 and 50, but noise is above 100, usually around 300-400.
func (s *StreamReader) isNoise(stream []int16) bool {
if len(stream) == 0 {
return false
}
prevsamp := stream[0]
switches := 0
// find the indexes where we cross the 0-boundary
for _, sample := range stream {
if (prevsamp > 0 && sample < 0) || (prevsamp < 0 && sample > 0) {
switches += 1
}
prevsamp = sample
}
switchrate := float32(switches) / float32(len(stream))
if DEBUG && LEVEL > 1 {
fmt.Printf("%0.0f ", switchrate*100)
}
return switchrate > 0.15
}
// bToInt16 converts bytes to int16
func (s *StreamReader) bToInt16(b []byte) (u []int16) {
u = make([]int16, len(b)/2)
for i, _ := range u {
val := int16(b[i*2])
val += int16(b[i*2+1]) << 8
u[i] = val
}
return
}