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"}, } )