2016-01-19 13:39:46 +00:00
|
|
|
/*
|
|
|
|
Copyright (c) 2015-2016 Christopher Young
|
|
|
|
Distributable under the terms of The "BSD New"" License
|
|
|
|
that can be found in the LICENSE file, herein included
|
|
|
|
as part of this header.
|
|
|
|
|
|
|
|
sdr.go: SDR monitoring, SDR management, data input from UAT/1090ES channels.
|
|
|
|
*/
|
|
|
|
|
2015-09-11 00:43:32 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"log"
|
2015-10-26 03:16:19 +00:00
|
|
|
"os/exec"
|
2016-02-16 20:28:14 +00:00
|
|
|
"regexp"
|
2015-10-26 03:16:19 +00:00
|
|
|
"strconv"
|
2016-02-16 20:28:14 +00:00
|
|
|
"strings"
|
2015-10-26 03:16:19 +00:00
|
|
|
"sync"
|
2015-11-07 03:57:59 +00:00
|
|
|
"sync/atomic"
|
2015-09-11 00:43:32 +00:00
|
|
|
"time"
|
2015-10-26 03:16:19 +00:00
|
|
|
|
|
|
|
"../godump978"
|
|
|
|
rtl "github.com/jpoirier/gortlsdr"
|
2015-09-11 00:43:32 +00:00
|
|
|
)
|
|
|
|
|
2016-03-08 01:53:50 +00:00
|
|
|
// Device holds per dongle values and attributes
|
2016-02-21 16:40:14 +00:00
|
|
|
type Device struct {
|
2015-10-26 03:16:19 +00:00
|
|
|
dev *rtl.Context
|
2016-02-21 16:40:14 +00:00
|
|
|
wg *sync.WaitGroup
|
|
|
|
closeCh chan int
|
2015-10-26 03:16:19 +00:00
|
|
|
indexID int
|
2016-02-13 20:30:33 +00:00
|
|
|
ppm int
|
2016-01-08 15:33:58 +00:00
|
|
|
serial string
|
2016-02-22 03:28:53 +00:00
|
|
|
idSet bool
|
2015-10-26 03:16:19 +00:00
|
|
|
}
|
|
|
|
|
2016-03-08 01:53:50 +00:00
|
|
|
// UAT is a 978 MHz device
|
2016-02-21 20:02:57 +00:00
|
|
|
type UAT Device
|
2016-03-08 01:53:50 +00:00
|
|
|
|
|
|
|
// ES is a 1090 MHz device
|
2016-02-21 20:02:57 +00:00
|
|
|
type ES Device
|
2016-02-21 16:55:16 +00:00
|
|
|
|
2016-03-08 01:53:50 +00:00
|
|
|
// UATDev holds a 978 MHz dongle object
|
2016-02-21 16:55:16 +00:00
|
|
|
var UATDev *UAT
|
2016-03-08 01:53:50 +00:00
|
|
|
|
|
|
|
// ESDev holds a 1090 MHz dongle object
|
2016-02-21 16:55:16 +00:00
|
|
|
var ESDev *ES
|
2015-09-22 13:52:49 +00:00
|
|
|
|
2016-03-26 21:12:26 +00:00
|
|
|
type Dump1090TermMessage struct {
|
|
|
|
Text string
|
|
|
|
Source string
|
|
|
|
}
|
|
|
|
|
2015-10-26 03:16:19 +00:00
|
|
|
func (e *ES) read() {
|
2016-02-21 16:40:14 +00:00
|
|
|
defer e.wg.Done()
|
2015-10-26 03:16:19 +00:00
|
|
|
log.Println("Entered ES read() ...")
|
2016-02-25 05:53:35 +00:00
|
|
|
cmd := exec.Command("/usr/bin/dump1090", "--oversample", "--net", "--device-index", strconv.Itoa(e.indexID), "--ppm", strconv.Itoa(e.ppm))
|
Fixes #102.
START,Thu Nov 12 19:25:38 +0000 UTC 2015,Thu Nov 12 19:25:38 +0000
UTC 2015
UNPAUSE,71289772
2739899291,Found 2 device(s):
2740283977,0: Realtek, RTL2838UHIDIR, SN: stratux:978
2740438248,1: Realtek, RTL2838UHIDIR, SN: 00000001 (currently
selected)
2740605904,Found Rafael Micro R820T tuner
2740683299,Max available gain is: 49.60
2740807935,Setting gain to: 49.60
2740880330,Exact sample rate is: 2000000.052982 Hz
2740943976,Gain reported by device: 49.60
2015-11-12 19:26:43 +00:00
|
|
|
stdout, _ := cmd.StdoutPipe()
|
|
|
|
stderr, _ := cmd.StderrPipe()
|
|
|
|
|
2015-10-26 03:16:19 +00:00
|
|
|
err := cmd.Start()
|
|
|
|
if err != nil {
|
2016-02-29 03:23:27 +00:00
|
|
|
log.Printf("Error executing /usr/bin/dump1090: %s\n", err)
|
2016-02-29 02:57:36 +00:00
|
|
|
// don't return immediately, use the proper shutdown procedure
|
2016-02-29 02:46:13 +00:00
|
|
|
shutdownES = true
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-e.closeCh:
|
|
|
|
return
|
|
|
|
default:
|
|
|
|
time.Sleep(1 * time.Second)
|
|
|
|
}
|
|
|
|
}
|
2015-09-11 00:43:32 +00:00
|
|
|
}
|
2016-02-29 02:46:13 +00:00
|
|
|
|
2015-10-26 03:16:19 +00:00
|
|
|
log.Println("Executed /usr/bin/dump1090 successfully...")
|
Fixes #102.
START,Thu Nov 12 19:25:38 +0000 UTC 2015,Thu Nov 12 19:25:38 +0000
UTC 2015
UNPAUSE,71289772
2739899291,Found 2 device(s):
2740283977,0: Realtek, RTL2838UHIDIR, SN: stratux:978
2740438248,1: Realtek, RTL2838UHIDIR, SN: 00000001 (currently
selected)
2740605904,Found Rafael Micro R820T tuner
2740683299,Max available gain is: 49.60
2740807935,Setting gain to: 49.60
2740880330,Exact sample rate is: 2000000.052982 Hz
2740943976,Gain reported by device: 49.60
2015-11-12 19:26:43 +00:00
|
|
|
|
2016-03-12 22:58:11 +00:00
|
|
|
done := make(chan bool)
|
2016-02-27 05:22:40 +00:00
|
|
|
|
2016-03-12 22:58:11 +00:00
|
|
|
go func() {
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-done:
|
|
|
|
return
|
|
|
|
case <-e.closeCh:
|
|
|
|
log.Println("ES read(): shutdown msg received, calling cmd.Process.Kill() ...")
|
|
|
|
err := cmd.Process.Kill()
|
|
|
|
if err == nil {
|
|
|
|
log.Println("\t kill successful...")
|
|
|
|
}
|
|
|
|
return
|
2016-03-13 02:11:33 +00:00
|
|
|
default:
|
|
|
|
time.Sleep(1 * time.Second)
|
2016-03-13 06:09:31 +00:00
|
|
|
}
|
2016-03-13 02:11:33 +00:00
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2016-03-14 07:23:03 +00:00
|
|
|
stdoutBuf := make([]byte, 1024)
|
|
|
|
stderrBuf := make([]byte, 1024)
|
2016-03-13 02:11:33 +00:00
|
|
|
go func() {
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-done:
|
|
|
|
return
|
2016-03-12 22:58:11 +00:00
|
|
|
default:
|
2016-03-14 07:23:03 +00:00
|
|
|
n, err := stdout.Read(stdoutBuf)
|
|
|
|
if err == nil && n > 0 {
|
2016-03-26 21:12:26 +00:00
|
|
|
m := Dump1090TermMessage{Text: string(stdoutBuf[:n]), Source: "stdout"}
|
|
|
|
logDump1090TermMessage(m)
|
2016-03-12 22:58:11 +00:00
|
|
|
}
|
2016-03-14 07:23:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
2016-02-27 05:22:40 +00:00
|
|
|
|
2016-03-14 07:23:03 +00:00
|
|
|
go func() {
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-done:
|
|
|
|
return
|
|
|
|
default:
|
|
|
|
n, err := stderr.Read(stderrBuf)
|
|
|
|
if err == nil && n > 0 {
|
2016-05-14 02:57:33 +00:00
|
|
|
m := Dump1090TermMessage{Text: string(stderrBuf[:n]), Source: "stderr"}
|
2016-03-26 21:12:26 +00:00
|
|
|
logDump1090TermMessage(m)
|
2016-03-12 22:58:11 +00:00
|
|
|
}
|
|
|
|
}
|
2015-10-26 03:16:19 +00:00
|
|
|
}
|
2016-03-12 22:58:11 +00:00
|
|
|
}()
|
|
|
|
|
|
|
|
cmd.Wait()
|
|
|
|
|
|
|
|
// we get here if A) the dump1090 process died
|
|
|
|
// on its own or B) cmd.Process.Kill() was called
|
|
|
|
// from within the goroutine, either way close
|
|
|
|
// the "done" channel, which ensures we don't leak
|
|
|
|
// goroutines...
|
|
|
|
close(done)
|
2015-10-26 03:16:19 +00:00
|
|
|
}
|
2015-09-11 00:43:32 +00:00
|
|
|
|
2015-10-26 03:16:19 +00:00
|
|
|
func (u *UAT) read() {
|
2016-02-21 16:40:14 +00:00
|
|
|
defer u.wg.Done()
|
2015-10-26 03:16:19 +00:00
|
|
|
log.Println("Entered UAT read() ...")
|
|
|
|
var buffer = make([]uint8, rtl.DefaultBufLength)
|
2015-11-17 19:31:21 +00:00
|
|
|
|
2015-10-26 03:16:19 +00:00
|
|
|
for {
|
|
|
|
select {
|
|
|
|
default:
|
|
|
|
nRead, err := u.dev.ReadSync(buffer, rtl.DefaultBufLength)
|
|
|
|
if err != nil {
|
2016-02-26 18:37:45 +00:00
|
|
|
if globalSettings.DEBUG {
|
|
|
|
log.Printf("\tReadSync Failed - error: %s\n", err)
|
|
|
|
}
|
2016-02-27 05:22:40 +00:00
|
|
|
if shutdownUAT != true {
|
|
|
|
shutdownUAT = true
|
|
|
|
}
|
2015-11-11 00:14:29 +00:00
|
|
|
break
|
2015-10-26 03:16:19 +00:00
|
|
|
}
|
2016-02-27 05:22:40 +00:00
|
|
|
|
2015-10-26 03:16:19 +00:00
|
|
|
if nRead > 0 {
|
|
|
|
buf := buffer[:nRead]
|
|
|
|
godump978.InChan <- buf
|
|
|
|
}
|
2016-02-21 16:40:14 +00:00
|
|
|
case <-u.closeCh:
|
2015-10-26 03:16:19 +00:00
|
|
|
log.Println("UAT read(): shutdown msg received...")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-15 18:21:43 +00:00
|
|
|
func getPPM(serial string) int {
|
2016-02-21 16:40:14 +00:00
|
|
|
r, err := regexp.Compile("str?a?t?u?x:\\d+:?(-?\\d*)")
|
2016-02-16 20:28:14 +00:00
|
|
|
if err != nil {
|
|
|
|
return globalSettings.PPM
|
|
|
|
}
|
|
|
|
|
2016-02-21 16:40:14 +00:00
|
|
|
arr := r.FindStringSubmatch(serial)
|
2016-02-15 18:21:43 +00:00
|
|
|
if arr == nil {
|
|
|
|
return globalSettings.PPM
|
2016-02-13 20:30:33 +00:00
|
|
|
}
|
|
|
|
|
2016-03-08 02:36:30 +00:00
|
|
|
ppm, err := strconv.Atoi(arr[1])
|
|
|
|
if err != nil {
|
2016-02-21 16:40:14 +00:00
|
|
|
return globalSettings.PPM
|
|
|
|
}
|
2016-03-08 01:53:50 +00:00
|
|
|
|
|
|
|
return ppm
|
2016-02-15 18:21:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (e *ES) sdrConfig() (err error) {
|
|
|
|
e.ppm = getPPM(e.serial)
|
2016-02-22 13:37:44 +00:00
|
|
|
log.Printf("===== ES Device Serial: %s PPM %d =====\n", e.serial, e.ppm)
|
2015-10-26 03:16:19 +00:00
|
|
|
return
|
|
|
|
}
|
2015-09-11 00:43:32 +00:00
|
|
|
|
2016-03-08 01:53:50 +00:00
|
|
|
// 978 UAT configuration settings
|
2016-02-21 16:40:14 +00:00
|
|
|
const (
|
|
|
|
TunerGain = 480
|
|
|
|
SampleRate = 2083334
|
|
|
|
NewRTLFreq = 28800000
|
|
|
|
NewTunerFreq = 28800000
|
|
|
|
CenterFreq = 978000000
|
|
|
|
Bandwidth = 1000000
|
|
|
|
)
|
|
|
|
|
2015-10-26 03:16:19 +00:00
|
|
|
func (u *UAT) sdrConfig() (err error) {
|
2016-02-22 16:34:51 +00:00
|
|
|
log.Printf("===== UAT Device Name : %s =====\n", rtl.GetDeviceName(u.indexID))
|
2016-02-22 13:37:44 +00:00
|
|
|
log.Printf("===== UAT Device Serial: %s=====\n", u.serial)
|
2016-02-13 20:30:33 +00:00
|
|
|
|
2015-10-26 03:16:19 +00:00
|
|
|
if u.dev, err = rtl.Open(u.indexID); err != nil {
|
|
|
|
log.Printf("\tUAT Open Failed...\n")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
log.Printf("\tGetTunerType: %s\n", u.dev.GetTunerType())
|
|
|
|
|
|
|
|
//---------- Set Tuner Gain ----------
|
|
|
|
err = u.dev.SetTunerGainMode(true)
|
2015-09-11 00:43:32 +00:00
|
|
|
if err != nil {
|
2015-10-26 03:16:19 +00:00
|
|
|
u.dev.Close()
|
2015-09-11 00:43:32 +00:00
|
|
|
log.Printf("\tSetTunerGainMode Failed - error: %s\n", err)
|
2015-10-26 03:16:19 +00:00
|
|
|
return
|
2015-09-11 00:43:32 +00:00
|
|
|
}
|
2016-03-08 01:53:50 +00:00
|
|
|
log.Printf("\tSetTunerGainMode Successful\n")
|
2015-09-11 00:43:32 +00:00
|
|
|
|
2016-02-21 16:40:14 +00:00
|
|
|
err = u.dev.SetTunerGain(TunerGain)
|
2015-09-11 00:43:32 +00:00
|
|
|
if err != nil {
|
2015-10-26 03:16:19 +00:00
|
|
|
u.dev.Close()
|
2015-09-11 00:43:32 +00:00
|
|
|
log.Printf("\tSetTunerGain Failed - error: %s\n", err)
|
2015-10-26 03:16:19 +00:00
|
|
|
return
|
2015-09-11 00:43:32 +00:00
|
|
|
}
|
2016-03-08 01:53:50 +00:00
|
|
|
log.Printf("\tSetTunerGain Successful\n")
|
2015-09-11 00:43:32 +00:00
|
|
|
|
2016-05-14 03:58:08 +00:00
|
|
|
tgain := u.dev.GetTunerGain()
|
|
|
|
log.Printf("\tGetTunerGain: %d\n", tgain)
|
|
|
|
|
2015-09-11 00:43:32 +00:00
|
|
|
//---------- Get/Set Sample Rate ----------
|
2016-02-21 16:40:14 +00:00
|
|
|
err = u.dev.SetSampleRate(SampleRate)
|
2015-09-11 00:43:32 +00:00
|
|
|
if err != nil {
|
2015-10-26 03:16:19 +00:00
|
|
|
u.dev.Close()
|
2015-09-11 00:43:32 +00:00
|
|
|
log.Printf("\tSetSampleRate Failed - error: %s\n", err)
|
2015-10-26 03:16:19 +00:00
|
|
|
return
|
2015-09-11 00:43:32 +00:00
|
|
|
}
|
2016-03-08 01:53:50 +00:00
|
|
|
log.Printf("\tSetSampleRate - rate: %d\n", SampleRate)
|
|
|
|
|
2015-10-26 03:16:19 +00:00
|
|
|
log.Printf("\tGetSampleRate: %d\n", u.dev.GetSampleRate())
|
2015-09-11 00:43:32 +00:00
|
|
|
|
|
|
|
//---------- Get/Set Xtal Freq ----------
|
2015-10-26 03:16:19 +00:00
|
|
|
rtlFreq, tunerFreq, err := u.dev.GetXtalFreq()
|
2015-09-11 00:43:32 +00:00
|
|
|
if err != nil {
|
2015-10-26 03:16:19 +00:00
|
|
|
u.dev.Close()
|
2015-09-11 00:43:32 +00:00
|
|
|
log.Printf("\tGetXtalFreq Failed - error: %s\n", err)
|
2015-10-26 03:16:19 +00:00
|
|
|
return
|
2015-09-11 00:43:32 +00:00
|
|
|
}
|
2016-03-08 01:53:50 +00:00
|
|
|
log.Printf("\tGetXtalFreq - Rtl: %d, Tuner: %d\n", rtlFreq, tunerFreq)
|
2015-09-11 00:43:32 +00:00
|
|
|
|
2016-02-21 16:40:14 +00:00
|
|
|
err = u.dev.SetXtalFreq(NewRTLFreq, NewTunerFreq)
|
2015-09-11 00:43:32 +00:00
|
|
|
if err != nil {
|
2015-10-26 03:16:19 +00:00
|
|
|
u.dev.Close()
|
2015-09-11 00:43:32 +00:00
|
|
|
log.Printf("\tSetXtalFreq Failed - error: %s\n", err)
|
2015-10-26 03:16:19 +00:00
|
|
|
return
|
2015-09-11 00:43:32 +00:00
|
|
|
}
|
2016-03-08 01:53:50 +00:00
|
|
|
log.Printf("\tSetXtalFreq - Center freq: %d, Tuner freq: %d\n",
|
|
|
|
NewRTLFreq, NewTunerFreq)
|
2015-09-11 00:43:32 +00:00
|
|
|
|
|
|
|
//---------- Get/Set Center Freq ----------
|
2016-02-21 16:40:14 +00:00
|
|
|
err = u.dev.SetCenterFreq(CenterFreq)
|
2015-09-11 00:43:32 +00:00
|
|
|
if err != nil {
|
2015-10-26 03:16:19 +00:00
|
|
|
u.dev.Close()
|
2015-09-11 00:43:32 +00:00
|
|
|
log.Printf("\tSetCenterFreq 978MHz Failed, error: %s\n", err)
|
2015-10-26 03:16:19 +00:00
|
|
|
return
|
2015-09-11 00:43:32 +00:00
|
|
|
}
|
2016-03-08 01:53:50 +00:00
|
|
|
log.Printf("\tSetCenterFreq 978MHz Successful\n")
|
2015-09-11 00:43:32 +00:00
|
|
|
|
2015-10-26 03:16:19 +00:00
|
|
|
log.Printf("\tGetCenterFreq: %d\n", u.dev.GetCenterFreq())
|
2015-09-11 00:43:32 +00:00
|
|
|
|
|
|
|
//---------- Set Bandwidth ----------
|
2016-02-21 16:40:14 +00:00
|
|
|
log.Printf("\tSetting Bandwidth: %d\n", Bandwidth)
|
|
|
|
if err = u.dev.SetTunerBw(Bandwidth); err != nil {
|
2015-10-26 03:16:19 +00:00
|
|
|
u.dev.Close()
|
2016-02-21 16:40:14 +00:00
|
|
|
log.Printf("\tSetTunerBw %d Failed, error: %s\n", Bandwidth, err)
|
2015-10-26 03:16:19 +00:00
|
|
|
return
|
2015-09-11 00:43:32 +00:00
|
|
|
}
|
2016-03-08 01:53:50 +00:00
|
|
|
log.Printf("\tSetTunerBw %d Successful\n", Bandwidth)
|
2015-09-11 00:43:32 +00:00
|
|
|
|
2015-10-26 03:16:19 +00:00
|
|
|
if err = u.dev.ResetBuffer(); err != nil {
|
|
|
|
u.dev.Close()
|
2015-09-11 00:43:32 +00:00
|
|
|
log.Printf("\tResetBuffer Failed - error: %s\n", err)
|
2015-10-26 03:16:19 +00:00
|
|
|
return
|
2015-09-11 00:43:32 +00:00
|
|
|
}
|
2016-03-08 01:53:50 +00:00
|
|
|
log.Printf("\tResetBuffer Successful\n")
|
2016-02-21 16:40:14 +00:00
|
|
|
|
2015-09-14 03:38:22 +00:00
|
|
|
//---------- Get/Set Freq Correction ----------
|
2015-10-26 03:16:19 +00:00
|
|
|
freqCorr := u.dev.GetFreqCorrection()
|
2015-09-14 03:38:22 +00:00
|
|
|
log.Printf("\tGetFreqCorrection: %d\n", freqCorr)
|
2016-02-22 03:28:53 +00:00
|
|
|
|
2016-02-21 16:40:14 +00:00
|
|
|
u.ppm = getPPM(u.serial)
|
2016-02-13 20:30:33 +00:00
|
|
|
err = u.dev.SetFreqCorrection(u.ppm)
|
2015-11-09 15:10:55 +00:00
|
|
|
if err != nil {
|
2015-11-09 15:14:08 +00:00
|
|
|
u.dev.Close()
|
2016-02-13 20:30:33 +00:00
|
|
|
log.Printf("\tSetFreqCorrection %d Failed, error: %s\n", u.ppm, err)
|
2015-11-09 15:14:08 +00:00
|
|
|
return
|
2015-09-14 03:38:22 +00:00
|
|
|
}
|
2016-03-08 01:53:50 +00:00
|
|
|
log.Printf("\tSetFreqCorrection %d Successful\n", u.ppm)
|
|
|
|
|
2015-10-26 03:16:19 +00:00
|
|
|
return
|
2015-09-11 00:43:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Read from the godump978 channel - on or off.
|
|
|
|
func uatReader() {
|
2015-10-26 03:16:19 +00:00
|
|
|
log.Println("Entered uatReader() ...")
|
2015-09-11 00:43:32 +00:00
|
|
|
for {
|
|
|
|
uat := <-godump978.OutChan
|
|
|
|
o, msgtype := parseInput(uat)
|
|
|
|
if o != nil && msgtype != 0 {
|
|
|
|
relayMessage(msgtype, o)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-11 18:54:42 +00:00
|
|
|
func (u *UAT) writeID() error {
|
|
|
|
info, err := u.dev.GetHwInfo()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
info.Serial = "stratux:978"
|
|
|
|
return u.dev.SetHwInfo(info)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *ES) writeID() error {
|
|
|
|
info, err := e.dev.GetHwInfo()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
info.Serial = "stratux:1090"
|
|
|
|
return e.dev.SetHwInfo(info)
|
|
|
|
}
|
|
|
|
|
2015-10-26 03:16:19 +00:00
|
|
|
func (u *UAT) shutdown() {
|
|
|
|
log.Println("Entered UAT shutdown() ...")
|
2016-02-21 16:40:14 +00:00
|
|
|
close(u.closeCh) // signal to shutdown
|
|
|
|
log.Println("UAT shutdown(): calling u.wg.Wait() ...")
|
|
|
|
u.wg.Wait() // Wait for the goroutine to shutdown
|
|
|
|
log.Println("UAT shutdown(): u.wg.Wait() returned...")
|
2015-12-21 04:38:42 +00:00
|
|
|
log.Println("UAT shutdown(): closing device ...")
|
|
|
|
u.dev.Close() // preempt the blocking ReadSync call
|
2016-03-12 01:29:46 +00:00
|
|
|
log.Println("UAT shutdown() complete ...")
|
2015-10-26 03:16:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (e *ES) shutdown() {
|
|
|
|
log.Println("Entered ES shutdown() ...")
|
2016-02-21 16:40:14 +00:00
|
|
|
close(e.closeCh) // signal to shutdown
|
|
|
|
log.Println("ES shutdown(): calling e.wg.Wait() ...")
|
|
|
|
e.wg.Wait() // Wait for the goroutine to shutdown
|
2016-03-12 01:29:46 +00:00
|
|
|
log.Println("ES shutdown() complete ...")
|
2015-10-26 03:16:19 +00:00
|
|
|
}
|
|
|
|
|
2016-01-19 15:40:40 +00:00
|
|
|
var sdrShutdown bool
|
|
|
|
|
|
|
|
func sdrKill() {
|
|
|
|
// Send signal to shutdown to sdrWatcher().
|
|
|
|
sdrShutdown = true
|
|
|
|
// Spin until all devices have been de-initialized.
|
|
|
|
for UATDev != nil || ESDev != nil {
|
|
|
|
time.Sleep(1 * time.Second)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-21 20:02:57 +00:00
|
|
|
func reCompile(s string) *regexp.Regexp {
|
2016-02-22 03:41:30 +00:00
|
|
|
// note , compile returns a nil pointer on error
|
2016-02-21 20:02:57 +00:00
|
|
|
r, _ := regexp.Compile(s)
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
|
|
|
|
type regexUAT regexp.Regexp
|
2016-02-22 03:28:53 +00:00
|
|
|
type regexES regexp.Regexp
|
2016-02-21 20:02:57 +00:00
|
|
|
|
|
|
|
var rUAT = (*regexUAT)(reCompile("str?a?t?u?x:978"))
|
2016-02-22 13:37:44 +00:00
|
|
|
var rES = (*regexES)(reCompile("str?a?t?u?x:1090"))
|
2016-02-21 16:40:14 +00:00
|
|
|
|
2016-02-22 03:41:30 +00:00
|
|
|
func (r *regexUAT) hasID(serial string) bool {
|
2016-02-21 16:40:14 +00:00
|
|
|
if r == nil {
|
2016-02-21 17:00:12 +00:00
|
|
|
return strings.HasPrefix(serial, "stratux:978")
|
2016-02-16 20:28:14 +00:00
|
|
|
}
|
2016-02-21 20:02:57 +00:00
|
|
|
return (*regexp.Regexp)(r).MatchString(serial)
|
2016-02-21 16:40:14 +00:00
|
|
|
}
|
2016-02-16 20:28:14 +00:00
|
|
|
|
2016-02-22 03:41:30 +00:00
|
|
|
func (r *regexES) hasID(serial string) bool {
|
2016-02-21 16:40:14 +00:00
|
|
|
if r == nil {
|
|
|
|
return strings.HasPrefix(serial, "stratux:1090")
|
2016-02-16 20:28:14 +00:00
|
|
|
}
|
2016-02-21 20:02:57 +00:00
|
|
|
return (*regexp.Regexp)(r).MatchString(serial)
|
2016-02-21 16:40:14 +00:00
|
|
|
}
|
|
|
|
|
2016-02-22 03:28:53 +00:00
|
|
|
func createUATDev(id int, serial string, idSet bool) error {
|
|
|
|
UATDev = &UAT{indexID: id, serial: serial}
|
|
|
|
if err := UATDev.sdrConfig(); err != nil {
|
|
|
|
log.Printf("UATDev.sdrConfig() failed: %s\n", err)
|
|
|
|
UATDev = nil
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
UATDev.wg = &sync.WaitGroup{}
|
|
|
|
UATDev.idSet = idSet
|
|
|
|
UATDev.closeCh = make(chan int)
|
|
|
|
UATDev.wg.Add(1)
|
|
|
|
go UATDev.read()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func createESDev(id int, serial string, idSet bool) error {
|
|
|
|
ESDev = &ES{indexID: id, serial: serial}
|
|
|
|
if err := ESDev.sdrConfig(); err != nil {
|
|
|
|
log.Printf("ESDev.sdrConfig() failed: %s\n", err)
|
|
|
|
ESDev = nil
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
ESDev.wg = &sync.WaitGroup{}
|
|
|
|
ESDev.idSet = idSet
|
|
|
|
ESDev.closeCh = make(chan int)
|
|
|
|
ESDev.wg.Add(1)
|
|
|
|
go ESDev.read()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-03-08 01:53:50 +00:00
|
|
|
func configDevices(count int, esEnabled, uatEnabled bool) {
|
2016-02-22 19:01:11 +00:00
|
|
|
// once the tagged dongles have been assigned, explicitly range over
|
|
|
|
// the remaining IDs and assign them to any anonymous dongles
|
2016-02-24 14:23:21 +00:00
|
|
|
unusedIDs := make(map[int]string)
|
2016-02-22 16:34:51 +00:00
|
|
|
|
|
|
|
// loop 1: assign tagged dongles
|
2016-02-22 03:28:53 +00:00
|
|
|
for i := 0; i < count; i++ {
|
|
|
|
_, _, s, err := rtl.GetDeviceUsbStrings(i)
|
|
|
|
if err == nil {
|
2016-05-14 01:42:05 +00:00
|
|
|
//FIXME: Trim NULL from the serial. Best done in gortlsdr, but putting this here for now.
|
|
|
|
s = strings.Trim(s, "\x00")
|
2016-02-24 14:18:40 +00:00
|
|
|
// no need to check if createXDev returned an error; if it
|
|
|
|
// failed to config the error is logged and we can ignore
|
|
|
|
// it here so it doesn't get queued up again
|
2016-03-08 01:53:50 +00:00
|
|
|
if uatEnabled && UATDev == nil && rUAT.hasID(s) {
|
2016-02-22 03:28:53 +00:00
|
|
|
createUATDev(i, s, true)
|
2016-03-08 01:53:50 +00:00
|
|
|
} else if esEnabled && ESDev == nil && rES.hasID(s) {
|
2016-02-22 03:28:53 +00:00
|
|
|
createESDev(i, s, true)
|
2016-02-22 16:34:51 +00:00
|
|
|
} else {
|
2016-02-24 14:18:40 +00:00
|
|
|
unusedIDs[i] = s
|
2016-02-22 03:28:53 +00:00
|
|
|
}
|
2016-02-22 16:34:51 +00:00
|
|
|
} else {
|
|
|
|
log.Printf("rtl.GetDeviceUsbStrings id %d: %s\n", i, err)
|
|
|
|
}
|
|
|
|
}
|
2016-02-22 03:28:53 +00:00
|
|
|
|
2016-03-03 02:22:50 +00:00
|
|
|
// loop 2: assign anonymous dongles but sanity check the serial ids
|
|
|
|
// so we don't cross config for dual assigned dongles. e.g. when two
|
|
|
|
// dongles are set to the same stratux id and the unconsumed,
|
|
|
|
// non-anonymous, dongle makes it to this loop.
|
2016-02-24 14:18:40 +00:00
|
|
|
for i, s := range unusedIDs {
|
2016-03-08 01:53:50 +00:00
|
|
|
if uatEnabled && UATDev == nil && !rES.hasID(s) {
|
2016-02-24 14:18:40 +00:00
|
|
|
createUATDev(i, s, false)
|
2016-03-08 01:53:50 +00:00
|
|
|
} else if esEnabled && ESDev == nil && !rUAT.hasID(s) {
|
2016-02-24 14:18:40 +00:00
|
|
|
createESDev(i, s, false)
|
2016-02-22 03:28:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-03 05:05:52 +00:00
|
|
|
// to keep our sync primitives synchronized, only exit a read
|
|
|
|
// method's goroutine via the close flag channel check, to
|
|
|
|
// include catastrophic dongle failures
|
2016-02-27 05:22:40 +00:00
|
|
|
var shutdownES bool
|
|
|
|
var shutdownUAT bool
|
|
|
|
|
2016-02-21 16:40:14 +00:00
|
|
|
// Watch for config/device changes.
|
|
|
|
func sdrWatcher() {
|
2016-02-22 03:28:53 +00:00
|
|
|
prevCount := 0
|
2016-03-08 01:53:50 +00:00
|
|
|
prevUATEnabled := false
|
|
|
|
prevESEnabled := false
|
2016-02-22 03:28:53 +00:00
|
|
|
|
2015-09-11 00:43:32 +00:00
|
|
|
for {
|
2015-10-26 03:16:19 +00:00
|
|
|
time.Sleep(1 * time.Second)
|
2016-01-19 15:40:40 +00:00
|
|
|
if sdrShutdown {
|
|
|
|
if UATDev != nil {
|
|
|
|
UATDev.shutdown()
|
|
|
|
UATDev = nil
|
|
|
|
}
|
|
|
|
if ESDev != nil {
|
|
|
|
ESDev.shutdown()
|
|
|
|
ESDev = nil
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
2016-02-22 03:28:53 +00:00
|
|
|
|
2016-02-27 06:02:18 +00:00
|
|
|
// true when a ReadSync call fails
|
2016-02-27 05:22:40 +00:00
|
|
|
if shutdownUAT {
|
|
|
|
UATDev.shutdown()
|
|
|
|
UATDev = nil
|
2016-02-27 06:00:53 +00:00
|
|
|
shutdownUAT = false
|
2016-02-27 05:22:40 +00:00
|
|
|
}
|
2016-02-27 06:02:18 +00:00
|
|
|
// true when we get stderr output
|
2016-02-27 05:22:40 +00:00
|
|
|
if shutdownES {
|
|
|
|
ESDev.shutdown()
|
|
|
|
ESDev = nil
|
|
|
|
shutdownES = false
|
|
|
|
}
|
|
|
|
|
2016-03-14 20:47:33 +00:00
|
|
|
// capture current state
|
2016-03-14 20:49:00 +00:00
|
|
|
esEnabled := globalSettings.ES_Enabled
|
|
|
|
uatEnabled := globalSettings.UAT_Enabled
|
2015-10-26 03:16:19 +00:00
|
|
|
count := rtl.GetDeviceCount()
|
2015-11-07 03:57:59 +00:00
|
|
|
atomic.StoreUint32(&globalStatus.Devices, uint32(count))
|
2015-10-26 03:16:19 +00:00
|
|
|
|
2016-03-16 02:41:47 +00:00
|
|
|
// support up to two dongles
|
2015-10-26 03:16:19 +00:00
|
|
|
if count > 2 {
|
|
|
|
count = 2
|
|
|
|
}
|
|
|
|
|
2016-03-14 20:47:33 +00:00
|
|
|
if count == prevCount && prevESEnabled == esEnabled && prevUATEnabled == uatEnabled {
|
2015-10-26 03:16:19 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2016-03-03 02:02:33 +00:00
|
|
|
// the device count or the global settings have changed, reconfig
|
|
|
|
if UATDev != nil {
|
|
|
|
UATDev.shutdown()
|
|
|
|
UATDev = nil
|
|
|
|
}
|
|
|
|
if ESDev != nil {
|
|
|
|
ESDev.shutdown()
|
|
|
|
ESDev = nil
|
2015-09-11 00:43:32 +00:00
|
|
|
}
|
2016-03-14 20:47:33 +00:00
|
|
|
configDevices(count, esEnabled, uatEnabled)
|
2015-10-26 03:16:19 +00:00
|
|
|
|
2016-02-22 03:28:53 +00:00
|
|
|
prevCount = count
|
2016-03-14 20:47:33 +00:00
|
|
|
prevUATEnabled = uatEnabled
|
|
|
|
prevESEnabled = esEnabled
|
2015-09-11 00:43:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func sdrInit() {
|
|
|
|
go sdrWatcher()
|
|
|
|
go uatReader()
|
|
|
|
go godump978.ProcessDataFromChannel()
|
2015-09-14 03:38:22 +00:00
|
|
|
}
|