github.com/la5nta/wl2k-go@v0.11.8/transport/ardop/frame.go (about) 1 // Copyright 2015 Martin Hebnes Pedersen (LA5NTA). All rights reserved. 2 // Use of this source code is governed by the MIT-license that can be 3 // found in the LICENSE file. 4 5 package ardop 6 7 import ( 8 "bufio" 9 "encoding/binary" 10 "errors" 11 "fmt" 12 "io" 13 "regexp" 14 ) 15 16 type frame interface{} 17 18 type dFrame struct { 19 dataType string 20 data []byte 21 } 22 23 func (f dFrame) ARQFrame() bool { return f.dataType == "ARQ" } 24 func (f dFrame) FECFrame() bool { return f.dataType == "FEC" } 25 func (f dFrame) ErrFrame() bool { return f.dataType == "ERR" } 26 func (f dFrame) IDFrame() bool { return f.dataType == "IDF" } 27 28 type cmdFrame string 29 30 func (f cmdFrame) Parsed() ctrlMsg { return parseCtrlMsg(string(f)) } 31 32 func writeCtrlFrame(isTCP bool, w io.Writer, format string, params ...interface{}) error { 33 var prefix string 34 if !isTCP { 35 prefix = "C:" 36 } 37 38 payload := fmt.Sprintf(format+"\r", params...) 39 _, err := fmt.Fprint(w, prefix+payload) 40 41 if !isTCP && err == nil { 42 sum := crc16Sum([]byte(payload)) 43 err = binary.Write(w, binary.BigEndian, sum) 44 } 45 46 return err 47 } 48 49 func readFrameOfType(fType byte, reader *bufio.Reader, isTCP bool) (frame, error) { 50 var err error 51 var data []byte 52 switch fType { 53 case '*': // !isTCP 54 fType, err = reader.ReadByte() 55 if err != nil { 56 return nil, err 57 } 58 reader.ReadByte() // Discard ';'. (TODO: Use reader.Discard(1) when we drop support for Go <= 1.4). 59 return readFrameOfType(fType, reader, isTCP) 60 case 'c': 61 data, err = reader.ReadBytes('\r') 62 case 'd': 63 // Peek length 64 peeked, err := reader.Peek(2) 65 if err != nil { 66 return nil, err 67 } 68 length := binary.BigEndian.Uint16(peeked) + 2 // +2 to include the length bytes 69 70 // actual data 71 data = make([]byte, length) 72 var n int 73 for read := 0; read < int(length) && err == nil; { 74 n, err = reader.Read(data[read:]) 75 read += n 76 } 77 default: 78 return nil, fmt.Errorf("Unexpected frame type %c", fType) 79 } 80 81 if err != nil { 82 return nil, err 83 } 84 85 // Verify CRC sums 86 if !isTCP { 87 sumBytes := make([]byte, 2) 88 reader.Read(sumBytes) 89 crc := binary.BigEndian.Uint16(sumBytes) 90 if crc16Sum(data) != crc { 91 return nil, ErrChecksumMismatch 92 } 93 } 94 95 switch fType { 96 case 'c': 97 data = data[:len(data)-1] // Trim \r 98 return cmdFrame(string(data)), nil 99 case 'd': 100 return dFrame{dataType: string(data[2:5]), data: data[5:]}, nil 101 default: 102 panic("not possible") 103 } 104 } 105 106 // Data example: " LA5NTA:[JP20QE] " 107 var reID = regexp.MustCompile(`(\w+)[:\s]*\[(\w+)\]`) 108 109 func parseIDFrame(df dFrame) (callsign, gridsquare string, err error) { 110 if !df.IDFrame() { 111 return "", "", errors.New("Unexpected frame type") 112 } 113 114 matches := reID.FindSubmatch(df.data) 115 if len(matches) != 3 { 116 return "", "", errors.New("Unexpected ID format") 117 } 118 119 return string(matches[1]), string(matches[2]), nil 120 }