163 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			163 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package gps
 | 
						||
 | 
						||
import (
 | 
						||
	"fmt"
 | 
						||
	"log"
 | 
						||
	"strings"
 | 
						||
	"time"
 | 
						||
)
 | 
						||
 | 
						||
type nmeaFlags int
 | 
						||
 | 
						||
const (
 | 
						||
	gga     nmeaFlags = 1 << iota // global positioning systemfix data
 | 
						||
	rmc                           // recommended minimumspecific	GPS/TRANSIT data
 | 
						||
	gpgsv                         // GPS satellites in view
 | 
						||
	gpgsa                         // GPS DOP and active satellites
 | 
						||
	vtg                           // track made good and ground speed
 | 
						||
	xfi                           // Global Positioning SystemExtended FixData.)Bit 6 – GLGSV (GLONASS satellites in view GLONASSfixesonly
 | 
						||
	glgsa                         // 1. GPS/2. Glonass/3. GALILE DOPandActiveSatellites.
 | 
						||
	gns                           // fix data for GNSS receivers; output for GPS, GLONASS, GALILEO
 | 
						||
	_                             // Reserved
 | 
						||
	gagsv                         // GALILEO satellites in view
 | 
						||
	_                             // Reserved
 | 
						||
	_                             // Reserved
 | 
						||
	_                             // Reserved
 | 
						||
	_                             // Reserved
 | 
						||
	_                             // Reserved
 | 
						||
	bdpqgsa                       // BEIDOU/QZSS DOP and activesatellites
 | 
						||
	bdpqgsv                       // BEIDOUQZSS satellites in view
 | 
						||
 | 
						||
	nmeaFlagsMask = (1 << 18) - 1
 | 
						||
 | 
						||
	collectTimeout = 5 * time.Second
 | 
						||
)
 | 
						||
 | 
						||
func secondCountDownTimer(title string, logger *log.Logger, t time.Duration) {
 | 
						||
	counter := 0
 | 
						||
	for {
 | 
						||
		if counter > int(t.Seconds()) {
 | 
						||
			break
 | 
						||
		}
 | 
						||
		logger.Printf("%s: %d/%f\n", title, counter, t.Seconds())
 | 
						||
		time.Sleep(time.Second)
 | 
						||
		counter += 1
 | 
						||
	}
 | 
						||
}
 | 
						||
 | 
						||
// Go... otherwise will throw warning
 | 
						||
var nmeaFlagsAll = gga | rmc | gpgsv | gpgsa | vtg | xfi | glgsa | gns | gagsv | bdpqgsa | bdpqgsv
 | 
						||
 | 
						||
func (g *gps) rawCollect(flags nmeaFlags) (string, error) {
 | 
						||
	// Need to omplement low level write/read here because collect must be atomic operation
 | 
						||
	// If other command is executed(write/read) it will read a part of nmea report and cause an error
 | 
						||
 | 
						||
	// Setup gps
 | 
						||
 | 
						||
	// Receive data from all contries' satelites, enable DPO(Dynamic Power Optimization) mode
 | 
						||
	// if resp, err := g.port.Send("AT+CGNSSMODE=15,1"); err != nil {
 | 
						||
	// 	return "", fmt.Errorf("AT+CGNSSMODE= request: %w", err)
 | 
						||
	// } else {
 | 
						||
	// 	if !resp.Check() {
 | 
						||
	// 		return "", fmt.Errorf("AT+CGNSSMODE= error response: %s", resp)
 | 
						||
	// 	}
 | 
						||
	// }
 | 
						||
	// Receive all types of data
 | 
						||
	// if resp, err := g.port.Send("AT+CGPSNMEA=200191"); err != nil {
 | 
						||
	// 	return "", fmt.Errorf("AT+CGPSNMEA= request: %w", err)
 | 
						||
	// } else {
 | 
						||
	// 	if !resp.Check() {
 | 
						||
	// 		return "", fmt.Errorf("AT+CGPSNMEA= error response: %s", resp)
 | 
						||
	// 	}
 | 
						||
	// }
 | 
						||
	// Set output rate to 10Hz
 | 
						||
	if resp, err := g.port.Send("AT+CGPSNMEARATE=1"); err != nil {
 | 
						||
		return "", fmt.Errorf("AT+CGPSNMEARATE= request: %w", err)
 | 
						||
	} else {
 | 
						||
		if !resp.Check() {
 | 
						||
			return "", fmt.Errorf("AT+CGPSNMEARATE= error response: %s", resp)
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	g.switchGpsMode(true)
 | 
						||
	g.port.Mutex().Lock()
 | 
						||
	s := g.port.SerialPort()
 | 
						||
	defer func() {
 | 
						||
		g.port.Mutex().Unlock()
 | 
						||
		g.switchGpsMode(false)
 | 
						||
	}()
 | 
						||
 | 
						||
	// Send AT+CGPSINFOCFG=255, flags
 | 
						||
	flags &= nmeaFlagsMask
 | 
						||
	if _, err := s.Write([]byte("AT+CGPSINFOCFG=1,31\r\n")); err != nil {
 | 
						||
		return "", fmt.Errorf("serial port write 1: %w", err)
 | 
						||
	}
 | 
						||
	// Do I need to read answer
 | 
						||
 | 
						||
	// Wait
 | 
						||
	secondCountDownTimer("Collecting NMEA data", g.logger, collectTimeout)
 | 
						||
 | 
						||
	// Send AT+CGPSINFOCFG=0, flags
 | 
						||
	if _, err := s.Write([]byte("AT+CGPSINFOCFG=0,31\r\n")); err != nil {
 | 
						||
		return "", fmt.Errorf("serial port write 2: %w", err)
 | 
						||
	}
 | 
						||
 | 
						||
	time.Sleep(100 * time.Millisecond) // To enshure
 | 
						||
 | 
						||
	// Read
 | 
						||
	outBuf := make([]byte, 0)
 | 
						||
	buf := make([]byte, 128)
 | 
						||
 | 
						||
readLoop:
 | 
						||
	for {
 | 
						||
		n, err := s.Read(buf)
 | 
						||
		if err != nil {
 | 
						||
			return string(outBuf), fmt.Errorf("serial port read: %w", err)
 | 
						||
		}
 | 
						||
		if n == 0 {
 | 
						||
			break readLoop
 | 
						||
		}
 | 
						||
		outBuf = append(outBuf, buf[:n]...)
 | 
						||
	}
 | 
						||
 | 
						||
	return string(outBuf), nil
 | 
						||
}
 | 
						||
 | 
						||
func (g *gps) collectNmeaReports(flags nmeaFlags) ([]string, error) {
 | 
						||
	// Raw collect
 | 
						||
	resp, err := g.rawCollect(flags)
 | 
						||
	if err != nil {
 | 
						||
		return nil, fmt.Errorf("raw collect: %w", err)
 | 
						||
	}
 | 
						||
 | 
						||
	// DEBUG
 | 
						||
	g.logger.Println("NMEA raw collect:", resp)
 | 
						||
 | 
						||
	// Right responce struct:
 | 
						||
	// \r\n
 | 
						||
	// OK
 | 
						||
	// \r\n
 | 
						||
	// (NMEA sentence)...
 | 
						||
	// \r\n
 | 
						||
	// OK
 | 
						||
	// \r\n
 | 
						||
	strs := strings.Split(strings.Replace(resp, "\r", "", -1), "\n")
 | 
						||
 | 
						||
	// Check
 | 
						||
	// Now wait for:
 | 
						||
	// OK
 | 
						||
	// (NMEA sentence)...
 | 
						||
	// OK
 | 
						||
	// if len(strs) < 2 {
 | 
						||
	// 	return nil, fmt.Errorf("responce too few rows: %d", len(strs))
 | 
						||
	// }
 | 
						||
	// if !(strs[0] == "OK" && strs[len(strs)-1] == "OK") {
 | 
						||
	// 	return nil, fmt.Errorf("not OK responce: [%  s]", strs)
 | 
						||
	// }
 | 
						||
 | 
						||
	// This... response is not stable
 | 
						||
	// Every time it gives one or two OK and in ramdom order
 | 
						||
	// So I will not check gor it
 | 
						||
	return strs, nil
 | 
						||
}
 |