package aprs import ( "errors" "fmt" ) var ( ErrLatitude = errors.New("aprs: invalid latitude") ErrLongitude = errors.New("aprs: invalid longitude") ) // Latitude is the north-south position. Positive values are North, negative are South. type Latitude float64 func LatitudeFromDMH(degrees, minutes, hundreths int, north bool) Latitude { v := float64(degrees) + float64(minutes)/60 + float64(hundreths)/6000 for v > 90 { v -= 180 } for v < -90 { v += 180 } if north { return Latitude(v) } return -Latitude(v) } func (lat Latitude) DMH() (degrees, minutes, hundreths int, north bool) { degrees = int(lat) minutes = int((float64(lat) - float64(degrees)) * 60) hundreths = int((float64(lat) - float64(degrees) - float64(minutes)/60) * 6000) if hundreths == 100 { hundreths = 0 minutes += 1 } if minutes == 60 { minutes = 0 degrees += 1 } north = lat >= 0 return } func (lat *Latitude) ParseCompressed(b []byte) error { if len(b) != 4 { return ErrLatitude } n, err := base91Decode(string(b)) if err != nil { return err } *lat = Latitude(90 - float64(n)/380926) return nil } func (lat *Latitude) ParseUncompressed(b []byte) error { if len(b) != 8 || b[4] != '.' { return ErrLatitude } var north bool switch b[7] { case 'N': north = true case 'S': north = false default: return ErrLatitude } var ( degrees, minutes, hundreths int err error ) if degrees, err = parseBytesWithSpaces(b[0:2]); err != nil { return err } if minutes, err = parseBytesWithSpaces(b[2:4]); err != nil { return err } if hundreths, err = parseBytesWithSpaces(b[5:7]); err != nil { return err } *lat = LatitudeFromDMH(degrees, minutes, hundreths, north) return nil } func (lat Latitude) Compressed(b []byte) { v := int((90 - float64(lat)) * 380926.0) base91Encode(b, v) } func (lat Latitude) Uncompressed(b []byte) { var ( degrees, minutes, hundreths, north = lat.DMH() v = fmt.Sprintf("%02d%02d.%02d", degrees, minutes, hundreths) ) if north { v += "N" } else { v += "S" } copy(b, []byte(b)) } // Longitude is the east-west position. Positive values are East, negative are West. type Longitude float64 func LongitudeFromDMH(degrees, minutes, hundreths int, east bool) Longitude { v := float64(degrees) + float64(minutes)/60 + float64(hundreths)/6000 for v > 180 { v -= 360 } for v < -180 { v += 360 } if east { return Longitude(v) } return -Longitude(v) } func (long Longitude) DMH() (degrees, minutes, hundreths int, east bool) { degrees = int(long) minutes = int((float64(long) - float64(degrees)) * 60) hundreths = int((float64(long) - float64(degrees) - float64(minutes)/60) * 6000) if hundreths == 100 { hundreths = 0 minutes += 1 } if minutes == 60 { minutes = 0 degrees += 1 } east = long >= 0 return } func (long *Longitude) ParseCompressed(b []byte) error { if len(b) != 4 { return ErrLatitude } n, err := base91Decode(string(b)) if err != nil { return err } *long = Longitude(float64(n)/190463.0 - 180) return nil } func (long *Longitude) ParseUncompressed(b []byte) error { if len(b) != 9 || b[5] != '.' { return ErrLongitude } var east bool switch b[8] { case 'E': east = true case 'W': east = false default: return ErrLongitude } var ( degrees, minutes, hundreths int err error ) if degrees, err = parseBytesWithSpaces(b[0:3]); err != nil { return err } if minutes, err = parseBytesWithSpaces(b[3:5]); err != nil { return err } if hundreths, err = parseBytesWithSpaces(b[6:8]); err != nil { return err } *long = LongitudeFromDMH(degrees, minutes, hundreths, east) return nil } func (long Longitude) Compressed(b []byte) { v := int((180 + float64(long)) * 190463) base91Encode(b, v) } func (long Longitude) Uncompressed(b []byte) { var ( degrees, minutes, hundreths, east = long.DMH() v = fmt.Sprintf("%03d%02d.%02d", degrees, minutes, hundreths) ) if east { v += "E" } else { v += "W" } copy(b, []byte(b)) }