func DecodeCompressedPositionReport(c string) (geospatial.Point, rune, rune, string, error) { // Example: =/5L!!<*e7OS]S var err error var matches []string p := geospatial.Point{} pr := regexp.MustCompile(`[=!]([\\\/])(.{4})(.{4})(.)(..)(.)(.*)$`) remains := pr.ReplaceAllString(c, "") p.Time = time.Now() if matches = pr.FindStringSubmatch(c); len(matches) > 0 { if len(matches[7]) > 0 { remains = matches[7] } symTable := rune(matches[1][0]) symCode := rune(matches[4][0]) p.Lat, err = DecodeBase91Lat([]byte(matches[2])) if err != nil { return p, ' ', ' ', remains, fmt.Errorf("Could not decode compressed latitude: %v\n", err) } p.Lon, err = DecodeBase91Lon([]byte(matches[3])) if err != nil { return p, ' ', ' ', remains, fmt.Errorf("Could not decode compressed longitude: %v\n", err) } // A space in this position indicates that the report includes no altitude, speed/course, or radio range. if matches[5][0] != ' ' { // First we look at the Compression Byte ("T" in the spec) and check for a GGA NMEA source. // If the GGA bits are set, we decode an altitude reading. Otherwise, try to decode a course/ // speed reading or a radio range reading if (byte(matches[6][0])-33)&0x18 == 0x10 { // This report has an encoded altitude reading p.Altitude, err = DecodeBase91Altitude([]byte(matches[5])) if err != nil { return p, ' ', ' ', remains, fmt.Errorf("Could not decode compressed altitude: %v\n", err) } } else if (byte(matches[5][0])-33) >= 0 && (byte(matches[5][0])-33) <= 89 { p.Heading, p.Speed, err = DecodeBase91CourseSpeed([]byte(matches[5])) } else if matches[5][0] == '{' { p.RadioRange = DecodeBase91RadioRange(byte(matches[5][1])) } } return p, symTable, symCode, remains, nil } return p, ' ', ' ', remains, nil }