Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simulation #51

Merged
merged 8 commits into from
Apr 17, 2016
4 changes: 3 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@ language: go
go:
- 1.2
- 1.3
- 1.4
- 1.4
- 1.5
- 1.6
118 changes: 118 additions & 0 deletions gen/gen.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package gen

import (
"crypto/rand"
"fmt"
"math"

"github.com/bemasher/rtlamr/crc"
)

func NewRandSCM() (pkt []byte, err error) {
bch := crc.NewCRC("BCH", 0, 0x6F63, 0)

pkt = make([]byte, 12)
_, err = rand.Read(pkt)
if err != nil {
return nil, err
}

pkt[0] = 0xF9
pkt[1] = 0x53
pkt[2] &= 0x07

checksum := bch.Checksum(pkt[2:10])
pkt[10] = uint8(checksum >> 8)
pkt[11] = uint8(checksum & 0xFF)

return
}

type ManchesterLUT [16]byte

func NewManchesterLUT() ManchesterLUT {
return ManchesterLUT{
85, 86, 89, 90, 101, 102, 105, 106, 149, 150, 153, 154, 165, 166, 169, 170,
}
}

func (lut ManchesterLUT) Encode(data []byte) (manchester []byte) {
manchester = make([]byte, len(data)<<1)

for idx := range data {
manchester[idx<<1] = lut[data[idx]>>4]
manchester[idx<<1+1] = lut[data[idx]&0x0F]
}

return
}

func UnpackBits(data []byte) []byte {
bits := make([]byte, len(data)<<3)

for idx, b := range data {
offset := idx << 3
for bit := 7; bit >= 0; bit-- {
bits[offset+(7-bit)] = (b >> uint8(bit)) & 0x01
}
}

return bits
}

func Upsample(bits []byte, factor int) []byte {
signal := make([]byte, len(bits)*factor)

for idx, b := range bits {
offset := idx * factor
for i := 0; i < factor; i++ {
signal[offset+i] = b
}
}

return signal
}

func CmplxOscillatorS8(samples int, freq float64, samplerate float64) []int8 {
signal := make([]int8, samples<<1)

for idx := 0; idx < samples<<1; idx += 2 {
s, c := math.Sincos(2 * math.Pi * float64(idx) * freq / samplerate)
signal[idx] = int8(s * 127.5)
signal[idx+1] = int8(c * 127.5)
}

return signal
}

func CmplxOscillatorU8(samples int, freq float64, samplerate float64) []uint8 {
signal := make([]uint8, samples<<1)

for idx := 0; idx < samples<<1; idx += 2 {
s, c := math.Sincos(2 * math.Pi * float64(idx) * freq / samplerate)
signal[idx] = uint8(s*127.5 + 127.5)
signal[idx+1] = uint8(c*127.5 + 127.5)
}

return signal
}

func CmplxOscillatorF64(samples int, freq float64, samplerate float64) []float64 {
signal := make([]float64, samples<<1)

for idx := 0; idx < len(signal); idx += 2 {
signal[idx], signal[idx+1] = math.Sincos(2 * math.Pi * float64(idx) * freq / samplerate)
}

return signal
}

func F64toU8(f64 []float64, u8 []byte) {
if len(f64) != len(u8) {
panic(fmt.Errorf("arrays must have same dimensions: %d != %d", len(f64), len(u8)))
}

for idx, val := range f64 {
u8[idx] = uint8(val*127.5 + 127.5)
}
}
194 changes: 194 additions & 0 deletions gen/gen_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
package gen

import (
"bytes"
"encoding/binary"
"io"
"log"
"math"
"math/rand"
"os"
"strconv"
"strings"
"testing"

"github.com/bemasher/rtlamr/crc"
"github.com/bemasher/rtlamr/parse"

_ "github.com/bemasher/rtlamr/scm"
)

func init() {
log.SetFlags(log.Lshortfile | log.Lmicroseconds)
}

func TestNewRandSCM(t *testing.T) {
bch := crc.NewCRC("BCH", 0, 0x6F63, 0)

for i := 0; i < 512; i++ {
scm, err := NewRandSCM()
if err != nil {
t.Fatal(err)
}

checksum := bch.Checksum(scm[2:])
if checksum != 0 {
t.Fatalf("Failed checksum: %04X\n", checksum)
}
}
}

func TestManchesterLUT(t *testing.T) {
lut := NewManchesterLUT()

recv := lut.Encode([]byte{0x00})
expt := []byte{0x55, 0x55}
if !bytes.Equal(recv, expt) {
t.Fatalf("Expected %02X got %02X\n", expt, recv)
}

recv = lut.Encode([]byte{0xF9, 0x53})
expt = []byte{0xAA, 0x96, 0x66, 0x5A}
if !bytes.Equal(recv, expt) {
t.Fatalf("Expected %02X got %02X\n", expt, recv)
}
}

func TestUnpackBits(t *testing.T) {
t.Logf("%d\n", UnpackBits([]byte{0xF9, 0x53}))
}

func TestUpsample(t *testing.T) {
t.Logf("%d\n", Upsample(UnpackBits([]byte{0xF9, 0x53}), 8))
}

func TestCmplxOscillatorS8(t *testing.T) {
t.SkipNow()
signalFile, err := os.Create("cmplxs8.bin")
if err != nil {
t.Fatal(err)
}
defer signalFile.Close()

err = binary.Write(signalFile, binary.BigEndian, CmplxOscillatorS8(1<<10, 5e3, 2.4e6))
if err != nil {
t.Fatal(err)
}
}

func TestCmplxOscillatorU8(t *testing.T) {
t.SkipNow()

signalFile, err := os.Create("cmplxu8.bin")
if err != nil {
t.Fatal(err)
}
defer signalFile.Close()

err = binary.Write(signalFile, binary.BigEndian, CmplxOscillatorU8(1<<10, 5e3, 2.4e6))
if err != nil {
t.Fatal(err)
}
}

type TestCase struct {
*io.PipeReader
Data []byte
SignalLevelIdx int
DecimationIdx int
}

func TestSCM(t *testing.T) {
genParser, err := parse.NewParser("scm", 72, 1)
if err != nil {
t.Fatal(err)
}

cfg := genParser.Cfg()
lut := NewManchesterLUT()

noisedB := -30.0
noiseAmp := math.Pow(10, noisedB/20)

testCases := make(chan TestCase)

signalLevels := []float64{-40, -35, -30, -25, -20, -15, -10, -5, 0}
decimationFactors := []int{1, 2, 3, 4, 6, 8, 9, 12, 18}

go func() {
var block []byte
noise := make([]byte, cfg.BlockSize2<<1)

for signalLevelIdx, signalLevel := range signalLevels {
for decimationIdx, _ := range decimationFactors {
for idx := 0; idx < 24; idx++ {
r, w := io.Pipe()

scm, _ := NewRandSCM()
testCases <- TestCase{r, scm, signalLevelIdx, decimationIdx}

manchester := lut.Encode(scm)
bits := Upsample(UnpackBits(manchester), 72<<1)

freq := rand.Float64() - 0.5*float64(cfg.SampleRate)
carrier := CmplxOscillatorF64(len(bits)>>1, freq, float64(cfg.SampleRate))

signalAmplitude := math.Pow(10, signalLevel/20)
for idx := range carrier {
carrier[idx] *= float64(bits[idx]) * signalAmplitude
carrier[idx] += (rand.Float64() - 0.5) * 2.0 * noiseAmp
}

if len(block) != len(carrier) {
block = make([]byte, len(carrier))
}
F64toU8(carrier, block)

w.Write(block)
for idx := range noise {
noise[idx] = byte((rand.Float64()-0.5)*2.0*noiseAmp*127.5 + 127.5)
}
w.Write(noise)
w.Close()
}
}
}
close(testCases)
}()

results := make([][]int, len(decimationFactors))
for idx := range results {
results[idx] = make([]int, len(signalLevels))
}

for testCase := range testCases {
p, err := parse.NewParser("scm", 72, decimationFactors[testCase.DecimationIdx])
if err != nil {
t.Fatal(err)
}

cfg := p.Cfg()
block := make([]byte, cfg.BlockSize2)

for {
_, err := testCase.Read(block)
indices := p.Dec().Decode(block)
for _ = range p.Parse(indices) {
results[testCase.DecimationIdx][testCase.SignalLevelIdx]++
}

if err == io.EOF {
testCase.Close()
break
}
}
}

for idx := range results {
var row []string
for _, count := range results[idx] {
row = append(row, strconv.Itoa(count))
}
t.Log(strings.Join(row, ","))
}
}