Files
ham/protocol/aprs/symbol.go
2026-02-14 15:59:31 +01:00

267 lines
8.5 KiB
Go

package aprs
import (
"encoding/json"
"fmt"
)
var emptySymbol Symbol
type Symbol [2]byte
func (s Symbol) IsPrimaryTable() bool { return s[0] != '\\' }
func (s Symbol) MarshalJSON() ([]byte, error) {
if s == emptySymbol {
return json.Marshal("")
}
return json.Marshal(string(s[:]))
}
func (s Symbol) get(idx int) (string, error) {
var m map[byte]map[int]string
if s.IsPrimaryTable() {
m = primarySymbol
} else {
m = alternateSymbol
}
n, ok := m[s[1]]
if !ok {
return "", fmt.Errorf("unknown symbol %x", s[1])
}
if i, ok := n[idx]; ok {
return i, nil
}
return "", fmt.Errorf("symbol doesn't have requested index: %v", n)
}
func (s Symbol) String() string {
hr, err := s.get(1)
if err != nil {
return err.Error()
}
return hr
}
func (s Symbol) SSID() (string, error) {
return s.get(2)
}
func (s Symbol) Emoji() (string, error) {
return s.get(3)
}
func IsValidCompressedSymTable(c byte) bool {
return c == '/' ||
c == '\\' ||
(c >= 0x41 && c <= 0x5a) ||
(c >= 0x61 && c <= 0x6a)
}
func IsValidUncompressedSymTable(c byte) bool {
return c == '/' ||
c == '\\' ||
(c >= 0x41 && c <= 0x5a) ||
(c >= 0x30 && c <= 0x39)
}
var (
// Source: http://www.aprs.org/symbols/symbolsX.txt
// 0: XYZ code
// 1: Human readable
// 2: SSID
// 3: Emoji
primarySymbol = map[byte]map[int]string{
'!': {0: "BB", 1: "Police, Sheriff", 3: ":cop:"},
'"': {0: "BC", 1: "reserved"},
'#': {0: "BD", 1: "Digi"},
'$': {0: "BE", 1: "Phone", 3: ":phone:"},
'%': {0: "BF", 1: "DX Cluster"},
'&': {0: "BG", 1: "HF Gateway"},
'\'': {0: "BH", 1: "Small Aircraft", 2: "11", 3: ":airplane:"},
'(': {0: "BI", 1: "Mobile Satellite Station", 3: ":satellite:"},
')': {0: "BJ", 1: "Wheelchair", 3: ":wheelchair:"},
'*': {0: "BK", 1: "Snowmobile"},
'+': {0: "BL", 1: "Red Cross"},
',': {0: "BM", 1: "Boy Scout"},
'-': {0: "BN", 1: "House QTH (VHF)"},
'.': {0: "BO", 1: "X"},
'/': {0: "BP", 1: "Red Dot"},
'0': {0: "P0", 1: "Circle (0)"},
'1': {0: "P1", 1: "Circle (1)"},
'2': {0: "P2", 1: "Circle (2)"},
'3': {0: "P3", 1: "Circle (3)"},
'4': {0: "P4", 1: "Circle (4)"},
'5': {0: "P5", 1: "Circle (5)"},
'6': {0: "P6", 1: "Circle (6)"},
'7': {0: "P7", 1: "Circle (7)"},
'8': {0: "P8", 1: "Circle (8)"},
'9': {0: "P9", 1: "Circle (9)"},
':': {0: "MR", 1: "Fire", 3: ":fire:"},
';': {0: "MS", 1: "Campground", 3: ":tent:"},
'<': {0: "MT", 1: "Motorcycle", 2: "10", 3: ":bike:"},
'=': {0: "MU", 1: "Railroad Engine", 3: ":train:"},
'>': {0: "MV", 1: "Car", 2: "9", 3: ":car:"},
'?': {0: "MW", 1: "File Server"},
'@': {0: "MX", 1: "HC Future"},
'A': {0: "PA", 1: "Aid Station", 3: ":hospital:"},
'B': {0: "PB", 1: "BBS or PBBS"},
'C': {0: "PC", 1: "Canoe"},
'D': {0: "PD"},
'E': {0: "PE", 1: "Eyeball"},
'F': {0: "PF", 1: "Tractor", 3: ":tractor:"},
'G': {0: "PG", 1: "Grid Square"},
'H': {0: "PH", 1: "Hotel", 3: ":hotel:"},
'I': {0: "PI", 1: "TCP/IP"},
'J': {0: "PJ"},
'K': {0: "PK", 1: "School", 3: ":school:"},
'L': {0: "PL", 1: "PC User", 3: ":computer:"},
'M': {0: "PM", 1: "MacAPRS", 3: ":computer:"},
'N': {0: "PN", 1: "NTS Station"},
'O': {0: "PO", 1: "Balloon", 2: "11", 3: ":airplane:"},
'P': {0: "PP", 1: "Police", 3: ":police_car:"},
'Q': {0: "PQ"},
'R': {0: "PR", 1: "Recreational Vehicle", 2: "13", 3: ":car:"},
'S': {0: "PS", 1: "Shuttle"},
'T': {0: "PT", 1: "SSTV"},
'U': {0: "PU", 1: "Bus", 2: "2", 3: ":bus:"},
'V': {0: "PV", 1: "ATV"},
'W': {0: "PW", 1: "National WX Service Site"},
'X': {0: "PX", 1: "Helo", 2: "6"},
'Y': {0: "PY", 1: "Yacht", 2: "5", 3: ":sailboat:"},
'Z': {0: "PZ", 1: "WinAPRS", 3: ":computer:"},
'[': {0: "HS", 1: "Human/Person", 2: "7", 3: ":running:"},
'\\': {0: "HT", 1: "DF Station"},
']': {0: "HU", 1: "Post Office", 3: ":post_office:"},
'^': {0: "HV", 1: "Large Aircraft", 3: ":airplane:"},
'_': {0: "HW", 1: "Weather Station", 3: ":cloud:"},
'`': {0: "HX", 1: "Dish Antenna", 3: ":satellite:"},
'a': {0: "LA", 1: "Ambulance", 2: "1", 3: ":ambulance:"},
'b': {0: "LB", 1: "Bike", 2: "4", 3: ":bike:"},
'c': {0: "LC", 1: "Incident Command Post"},
'd': {0: "LD", 1: "Fire Dept", 3: ":fire_engine:"},
'e': {0: "LE", 1: "Horse", 3: ":racehorse:"},
'f': {0: "LF", 1: "Fire Truck", 2: "3", 3: ":fire_engine:"},
'g': {0: "LG", 1: "Glider", 3: ":airplane:"},
'h': {0: "LH", 1: "Hospital", 3: ":hospital:"},
'i': {0: "LI", 1: "IOTA"},
'j': {0: "LJ", 1: "Jeep", 2: "12", 3: ":car:"},
'k': {0: "LK", 1: "Truck", 2: "14", 3: ":truck:"},
'l': {0: "LL", 1: "Laptop", 3: ":computer:"},
'm': {0: "LM", 1: "Mic-E Repeater"},
'n': {0: "LN", 1: "Node"},
'o': {0: "LO", 1: "EOC"},
'p': {0: "LP", 1: "Dog", 3: ":dog2:"},
'q': {0: "LQ", 1: "Grid SQ"},
'r': {0: "LR", 1: "Repeater"},
's': {0: "LS", 1: "Ship", 2: "8", 3: ":ship:"},
't': {0: "LT", 1: "Truck Stop"},
'u': {0: "LU", 1: "Truck (18 Wheeler)", 3: ":truck:"},
'v': {0: "LV", 1: "Van", 2: "15", 3: ":minibus:"},
'w': {0: "LW", 1: "Water Station"},
'x': {0: "LX", 1: "xAPRS", 3: ":computer:"},
'y': {0: "LY", 1: "Yagi @ QTH"},
'z': {0: "LZ"},
'{': {0: "J1"},
'|': {0: "J2", 1: "TNC Stream Switch"},
'}': {0: "J3"},
'~': {0: "J4", 1: "TNC Stream Switch"},
}
alternateSymbol = map[byte]map[int]string{
'!': {0: "OBO", 1: "Emergency"},
'"': {0: "OC", 1: "Reserved"},
'#': {0: "OD#", 1: "Overlay Digi"},
'$': {0: "OEO", 1: "Bank/ATM", 3: ":atm:"},
'%': {0: "OFO", 1: "Power Plant", 3: ":factory:"},
'&': {0: "OG#", 1: "I=Igte R=RX T=1hopTX 2=2hopTX"},
'\'': {0: "OHO", 1: "Crash Site"},
'(': {0: "OIO", 1: "Cloudy", 3: ":cloud:"},
')': {0: "OJO", 1: "Firenet MEO"},
'*': {0: "OK"},
'+': {0: "OL", 1: "Church", 3: ":church:"},
',': {0: "OM", 1: "Girl Scouts", 3: ":tent:"},
'-': {0: "ONO", 1: "House", 3: ":house:"},
'.': {0: "OO", 1: "Ambiguous"},
'/': {0: "OP", 1: "Waypoint Destination"},
'0': {0: "A0#", 1: "Circle", 3: ":red_circle:"},
'1': {0: "A1"},
'2': {0: "A2"},
'3': {0: "A3"},
'4': {0: "A4"},
'5': {0: "A5"},
'6': {0: "A6"},
'7': {0: "A7"},
'8': {0: "A8O", 1: "WiFi Network"},
'9': {0: "A9", 1: "Gas Station", 3: ":fuelpump:"},
':': {0: "NR"},
';': {0: "NSO", 1: "Park/Picnic"},
'<': {0: "NTO", 1: "Advisory"},
'=': {0: "NUO"},
'>': {0: "NV#", 1: "Cars & Vehicles", 3: ":car:"},
'?': {0: "NW", 1: "Info Kiosk"},
'@': {0: "NX", 1: "Hurricane", 3: ":cyclone:"},
'A': {0: "AA#", 1: "Box DTMF & RFID"},
'B': {0: "AB"},
'C': {0: "AC", 1: "Coast Guard"},
'D': {0: "ADO", 1: "Depots"},
'E': {0: "AE", 1: "Smoke"},
'F': {0: "AF"},
'G': {0: "AG"},
'H': {0: "AHO", 1: "Haze"},
'I': {0: "AI", 1: "Rain Shower", 3: ":umbrella:"},
'J': {0: "AJ"},
'K': {0: "AK", 1: "Kenwood HT"},
'L': {0: "AL", 1: "Lighthouse"},
'M': {0: "AMO", 1: "MARS"},
'N': {0: "AN", 1: "Navigation Buoy"},
'O': {0: "AO", 1: "Rocket", 3: ":rocket:"},
'P': {0: "AP", 1: "Parking", 3: ":parking:"},
'Q': {0: "AQ", 1: "Quake"},
'R': {0: "ARO", 1: "Restaurant"},
'S': {0: "AS", 1: "Satellite/Pacsat", 3: ":rocket:"},
'T': {0: "AT", 1: "Thunderstorm"},
'U': {0: "AU", 1: "Sunny"},
'V': {0: "AV", 1: "VORTAC Nav Aid"},
'W': {0: "AW#", 1: "NWS Site"},
'X': {0: "AX", 1: "Pharmacy"},
'Y': {0: "AYO", 1: "Radios and devices"},
'Z': {0: "AZ"},
'[': {0: "DSO", 1: "W. Cloud"},
'\\': {0: "DTO", 1: "GPS"},
']': {0: "DU"},
'^': {0: "DV#", 1: "Other Aircraft", 3: ":airplane:"},
'_': {0: "DW#", 1: "WX Site"},
'`': {0: "DX", 1: "Rain", 3: ":umbrella:"},
'a': {0: "SA#O"},
'b': {0: "SB"},
'c': {0: "SC#O", 1: "CD Triangle"},
'd': {0: "SD", 1: "DX Spot"},
'e': {0: "SE", 1: "Sleet"},
'f': {0: "SF", 1: "Funnel Cloud"},
'g': {0: "SG", 1: "Gale Flags"},
'h': {0: "SHO", 1: "Store or Hamfest"},
'i': {0: "SI#", 1: "Box / POI"},
'j': {0: "SJ", 1: "Work Zone"},
'k': {0: "SKO", 1: "Special Vehicle"},
'l': {0: "SL", 1: "Areas"},
'm': {0: "SM", 1: "Value Sign"},
'n': {0: "SN#", 1: "Triangle"},
'o': {0: "SO", 1: "Small Circle"},
'p': {0: "SP"},
'q': {0: "SQ"},
'r': {0: "SR", 1: "Restrooms"},
's': {0: "SS#", 1: "Ship/Boats", 3: ":speedboat:"},
't': {0: "ST", 1: "Tornado", 3: ":cyclone:"},
'u': {0: "SU#", 1: "Truck", 3: ":truck:"},
'v': {0: "SV#", 1: "Van", 3: ":minibus:"},
'w': {0: "SWO", 1: "Flooding"},
'x': {0: "SX", 1: "Wreck/Obstruction"},
'y': {0: "SY", 1: "Skywarn"},
'z': {0: "SZ#", 1: "Shelter"},
'{': {0: "Q1"},
'|': {0: "Q2", 1: "TNC Stream Switch"},
'}': {0: "Q3"},
'~': {0: "Q4", 1: "TNC Stream Switch"},
}
)