/* Copyright (c) 2016 uAvionix Distributable under the terms of The "BSD New"" License that can be found in the LICENSE file, herein included as part of this header. ping.go: uAvionix Ping ADS-B monitoring and management. */ package main import ( "bufio" //"fmt" "log" "os" "strings" "sync" //"sync/atomic" "net" "os/exec" "time" // Using forked version of tarm/serial to force Linux // instead of posix code, allowing for higher baud rates "github.com/uavionix/serial" ) // Ping device data var pingSerialConfig *serial.Config var pingSerialPort *serial.Port var pingWG *sync.WaitGroup var closeCh chan int func initPingSerial() bool { var device string baudrate := int(2000000) log.Printf("Configuring Ping ADS-B\n") if _, err := os.Stat("/dev/ping"); err == nil { device = "/dev/ping" } else { log.Printf("No suitable Ping device found.\n") return false } log.Printf("Using %s for Ping\n", device) // Open port // No timeout specified as Ping does not heartbeat pingSerialConfig = &serial.Config{Name: device, Baud: baudrate} p, err := serial.OpenPort(pingSerialConfig) if err != nil { log.Printf("Error opening serial port: %s\n", err.Error()) return false } log.Printf("Ping opened serial port") // No device configuration is needed, we should be ready pingSerialPort = p return true } func pingNetworkRepeater() { defer pingWG.Done() log.Println("Entered Ping network repeater ...") cmd := exec.Command("/usr/bin/dump1090", "--net-only") stdout, _ := cmd.StdoutPipe() stderr, _ := cmd.StderrPipe() err := cmd.Start() if err != nil { log.Printf("Error executing /usr/bin/dump1090: %s\n", err) // don't return immediately, use the proper shutdown procedure shutdownPing = true for { select { case <-closeCh: return default: time.Sleep(1 * time.Second) } } } log.Println("Executed /usr/bin/dump1090 successfully...") scanStdout := bufio.NewScanner(stdout) scanStderr := bufio.NewScanner(stderr) for { select { case <-closeCh: log.Println("Ping network repeater: shutdown msg received, calling cmd.Process.Kill() ...") err := cmd.Process.Kill() if err != nil { log.Printf("\t couldn't kill dump1090: %s\n", err) } else { cmd.Wait() log.Println("\t kill successful...") } return default: for scanStdout.Scan() { m := Dump1090TermMessage{Text: scanStdout.Text(), Source: "stdout"} logDump1090TermMessage(m) } if err := scanStdout.Err(); err != nil { log.Printf("scanStdout error: %s\n", err) } for scanStderr.Scan() { m := Dump1090TermMessage{Text: scanStderr.Text(), Source: "stderr"} logDump1090TermMessage(m) if shutdownES != true { shutdownES = true } } if err := scanStderr.Err(); err != nil { log.Printf("scanStderr error: %s\n", err) } time.Sleep(1 * time.Second) } } } var dump1090Connection net.Conn = nil var connectionError error func pingNetworkConnection() { // Send to dump1090 on port 30001 dump1090Addr := "127.0.0.1:30001" dump1090Connection, connectionError = net.Dial("tcp", dump1090Addr) // RCB monitor for connection failure and redial } func pingSerialReader() { //defer pingWG.Done() defer pingSerialPort.Close() // RCB TODO channel control for terminate log.Printf("Starting Ping serial reader") scanner := bufio.NewScanner(pingSerialPort) for scanner.Scan() && globalStatus.Ping_connected && globalSettings.Ping_Enabled { s := scanner.Text() // Trimspace removes newlines as well as whitespace s = strings.TrimSpace(s) //logString := fmt.Sprintf("Ping received: %s", s) //log.Println(logString) if s[0] == '*' { // 1090ES report // Ping appends a signal strength at the end of the message // e.g. *8DC01C2860C37797E9732E555B23;ss=049D; // Remove this before forwarding to dump1090 // We currently aren't doing anything with this information // and need to develop a scaling equation - we're using a // log detector for power so it should have a logarithmic // relationship. In one example, at -25dBm input (upper limit // of RX) we saw ~0x500. At -95dBm input (lower limit of RX) // we saw 0x370 report := strings.Split(s, ";") //replayLog(s, MSGCLASS_DUMP1090); if dump1090Connection == nil { log.Println("Starting dump1090 network connection") pingNetworkConnection() } if len(report[0]) != 0 && dump1090Connection != nil { dump1090Connection.Write([]byte(report[0] + ";\r\n")) //log.Println("Relaying 1090ES message") //logString := fmt.Sprintf("Relaying 1090ES: %s;", report[0]); //log.Println(logString) } } else if s[0] == '+' || s[0] == '-' { // UAT report // Ping appends a signal strength and RS bit errors corrected // at the end of the message // e.g. -08A5DFDF3907E982585F029B00040080105C3AB4BC5C240700A206000000000000003A13C82F96C80A63191F05FCB231;rs=1;ss=A2; // We need to rescale the signal strength for interpretation by dump978, // which expects a 0-1000 base 10 (linear?) scale // RSSI is in hex and represents an int8 with -128 (0x80) representing an // errored measurement. There will be some offset from actual due to loss // in the path. In one example we measured 0x93 (-98) when injecting a // -102dBm signal o, msgtype := parseInput(s) if o != nil && msgtype != 0 { //logString = fmt.Sprintf("Relaying message, type=%d", msgtype) //log.Println(logString) relayMessage(msgtype, o) } else if o == nil { //log.Println("Not relaying message, o == nil") } else { //log.Println("Not relaying message, msgtype == 0") } } } globalStatus.Ping_connected = false log.Printf("Exiting Ping serial reader") return } func pingShutdown() { log.Println("Entered Ping shutdown() ...") //close(closeCh) //log.Println("Ping shutdown(): calling pingWG.Wait() ...") //pingWG.Wait() // Wait for the goroutine to shutdown //log.Println("Ping shutdown(): pingWG.Wait() returned...") // RCB TODO FINISH globalStatus.Ping_connected = false } func pingKill() { // Send signal to shutdown to pingWatcher(). shutdownPing = true // Spin until device has been de-initialized. for globalStatus.Ping_connected != false { time.Sleep(1 * time.Second) } } // to keep our sync primitives synchronized, only exit a read // method's goroutine via the close flag channel check, to // include catastrophic dongle failures var shutdownPing bool // Watch for config/device changes. func pingWatcher() { prevPingEnabled := false for { time.Sleep(1 * time.Second) // true when a serial call fails if shutdownPing { pingShutdown() shutdownPing = false } if prevPingEnabled == globalSettings.Ping_Enabled { continue } // Global settings have changed, reconfig if globalSettings.Ping_Enabled && !globalStatus.Ping_connected { globalStatus.Ping_connected = initPingSerial() //count := 0 if globalStatus.Ping_connected { //pingWG.Add(1) go pingNetworkRepeater() //pingNetworkConnection() go pingSerialReader() // Emulate SDR count //count = 2 } //atomic.StoreUint32(&globalStatus.Devices, uint32(count)) } else if !globalSettings.Ping_Enabled { pingShutdown() } prevPingEnabled = globalSettings.Ping_Enabled } } func pingInit() { go pingWatcher() }