220 lines
4.0 KiB
Go
220 lines
4.0 KiB
Go
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))
|
|
}
|