From aa5769263273980f82739174f705846705280a41 Mon Sep 17 00:00:00 2001 From: Jason Playne Date: Fri, 1 Dec 2023 09:08:47 +0800 Subject: [PATCH] correct decoding of 13 bit altitude fields when reporting in 100ft increments --- lib/tracker/mode_s/common.go | 43 +++++------------- lib/tracker/mode_s/decode.go | 72 +++++++++++++------------------ lib/tracker/mode_s/decode_test.go | 62 +++++--------------------- lib/tracker/mode_s/frame.go | 1 + 4 files changed, 51 insertions(+), 127 deletions(-) diff --git a/lib/tracker/mode_s/common.go b/lib/tracker/mode_s/common.go index 90d43c11..35755005 100644 --- a/lib/tracker/mode_s/common.go +++ b/lib/tracker/mode_s/common.go @@ -3,8 +3,8 @@ package mode_s // var format string = "%20s = %13s" // decode an AC12 altitude field -func decodeAC12Field(AC12Field int32) int32 { - qBit := (AC12Field & 0x10) == 0x10 +func decodeAC12Field(aC12Field int32) int32 { + qBit := (aC12Field & 0x10) == 0x10 var n int32 // log.Printf(format, "0x10", strconv.FormatInt(int64(0x10), 2)) // log.Printf(format, "AC12", strconv.FormatInt(int64(AC12Field), 2)) @@ -12,40 +12,19 @@ func decodeAC12Field(AC12Field int32) int32 { if qBit { // log.Printf(format, "Q Bit Set", strconv.FormatInt(int64(AC12Field), 2)) /// N is the 11 bit integer resulting from the removal of bit Q at bit 4 - n = ((AC12Field & 0x0FE0) >> 1) | (AC12Field & 0x000F) + n = ((aC12Field & 0x0FE0) >> 1) | (aC12Field & 0x000F) // The final altitude is the resulting number multiplied by 25, minus 1000. return (n * 25) - 1000 - } else { - // Make N a 13 bit Gillham coded altitude by inserting M=0 at bit 6 - n = ((AC12Field & 0x0FC0) << 1) | (AC12Field & 0x003F) - // log.Printf(format, "Q Bit Clear", strconv.FormatInt(int64(n), 2)) - n = modeAToModeC(decodeID13Field(n)) - if n < -12 { - n = 0 - } - return int32(100 * n) } -} - -// this code liberally lifted from: http://www.ccsinfo.com/forum/viewtopic.php?p=77544 -func gillhamToAltitude(i16GillhamValue int32) int32 { - var i32Result int32 - var i16TempResult int32 - - // Convert Gillham value using gray code to binary conversion algorithm. - i16TempResult = i16GillhamValue ^ (i16GillhamValue >> 8) - i16TempResult ^= i16TempResult >> 4 - i16TempResult ^= i16TempResult >> 2 - i16TempResult ^= i16TempResult >> 1 - - // Convert gray code converted binary to altitude offset. - i16TempResult -= ((i16TempResult >> 4) * 6) + (((i16TempResult % 16) / 5) * 2) - - // Convert altitude offset to altitude. - i32Result = (i16TempResult - 13) * 100 - - return i32Result + // Make N a 13 bit Gillham coded altitude by inserting M=0 at bit 6 + n = ((aC12Field & 0x0FC0) << 1) | (aC12Field & 0x003F) + // log.Printf(format, "Q Bit Clear", strconv.FormatInt(int64(n), 2)) + n = modeAToModeC(decodeID13Field(n)) + if n < -12 { + n = 0 + } + return int32(100 * n) } func decodeID13Field(ID13Field int32) int32 { diff --git a/lib/tracker/mode_s/decode.go b/lib/tracker/mode_s/decode.go index ffa94e6e..b9cf13ef 100644 --- a/lib/tracker/mode_s/decode.go +++ b/lib/tracker/mode_s/decode.go @@ -162,7 +162,7 @@ func (f *Frame) parseIntoRaw() error { return nil } encodedFrame := strings.TrimFunc(f.full, func(r rune) bool { - return unicode.IsSpace(r) || ';' == r + return unicode.IsSpace(r) || r == ';' }) // let's ensure that we have some correct data... @@ -219,7 +219,7 @@ func (f *Frame) parseRadarcapeTimeStamp() { } func (f *Frame) parseBeastTimeStamp() error { - if "" == f.beastTimeStamp || "00000000000" == f.beastTimeStamp { + if f.beastTimeStamp == "" || "00000000000" == f.beastTimeStamp { return nil } // MLAT timestamps from Beast AVR are dependent on when the device started ( 500ns intervals / 12mhz) @@ -420,67 +420,60 @@ func (f *Frame) decodeSquawkIdentity(byte1, byte2 int) { // the 1 bits are AC13 field // 00000000 00000000 00011111 1M1Q1111 00000000 func (f *Frame) decode13bitAltitudeCode() error { - f.ac = uint32(f.message[2]&0xf)<<8 | uint32(f.message[3]) - // altitude + // altitude type, M Bit f.acM = f.ac&0x40 == 0x40 // bit 26 of message. 0 == feet, 1 = metres - // resolution + // resolution Q bit f.acQ = f.ac&0x10 == 0x10 // bit 28 of message. 1 = 25 ft encoding, 0 = Gillham Mode C encoding // make sure all the bits are good - if !f.acM { + switch { + case !f.acM && f.acQ: + // 25 ft increments f.unit = modesUnitFeet - - /* N is the 11 bit integer resulting from the removal of bit Q and M */ - var msg2 = int32(f.message[2]) - var msg3 = int32(f.message[3]) - var n = int32((msg2&31)<<6) | ((msg3 & 0x80) >> 2) | ((msg3 & 0x20) >> 1) | (msg3 & 15) - - if f.acQ { - // 25 ft increments - /* The final altitude is due to the resulting number multiplied - * by 25, minus 1000. */ - f.altitude = (n * 25) - 1000 - f.validAltitude = true - } else { - // altitude reported in feet, 100ft increments - /* Annex 10 — Aeronautical Telecommunications: - SSR automatic pressure-altitude transmission code (pulse position assignment) - */ - /* If the M bit (bit 26) and the Q bit (bit 28) equal 0, the altitude shall be coded according to the - pattern for Mode C replies of 3.1.1.7.12.2.3. Starting with bit 20 the sequence shall be - C1, A1, C2, A2, C4, A4, ZERO, B1, ZERO, B2, D2, B4, D4. - */ - - f.altitude = gillhamToAltitude(n) - f.validAltitude = true + /* `n` is the 11 bit integer resulting from the removal of bit Q and M */ + var n = int32(((f.ac & 0x1F80) >> 2) | ((f.ac & 0x0020) >> 1) | (f.ac & 0x000F)) + /* The final altitude is due to the resulting number multiplied by 25, minus 1000. */ + f.altitude = (n * 25) - 1000 + f.validAltitude = true + + case !f.acM && !f.acQ: + // altitude reported in feet, 100ft increments + f.unit = modesUnitFeet + f.altitude = modeAToModeC(decodeID13Field(int32(f.ac))) + f.validAltitude = f.altitude >= -12 + if !f.validAltitude { + f.altitude = 0 } - } else { + f.altitude *= 100 + + case f.acM: + // we are dealing with metres f.unit = modesUnitMetres + f.validAltitude = false + //TODO: Implement decoding Metres } + return nil } func (f *Frame) getMessageLengthBits() uint32 { - //if f.downLinkFormat & 0x10 != 0 { if f.downLinkFormat&0x10 != 0 { if len(f.message) == 14 { return modesShortMsgBits } return modesLongMsgBits - } else { - return modesShortMsgBits } + return modesShortMsgBits } func (f *Frame) getMessageLengthBytes() uint32 { if f.downLinkFormat&0x10 != 0 { return modesLongMsgBytes - } else { - return modesShortMsgBytes } + return modesShortMsgBytes } func (f *Frame) decodeFlightNumber() { @@ -508,10 +501,3 @@ func decodeFlightNumber(b []byte) []byte { } return callsign } - -//func (f *Frame) decodeFlightId() { -// if f.message[4] == 32 && len(f.message) >= 10 { -// // Aircraft Identification -// f.decodeFlightNumber() -// } -//} diff --git a/lib/tracker/mode_s/decode_test.go b/lib/tracker/mode_s/decode_test.go index 2e58c90b..b1190a3d 100644 --- a/lib/tracker/mode_s/decode_test.go +++ b/lib/tracker/mode_s/decode_test.go @@ -71,7 +71,6 @@ func TestDecodeString_DF17_EVEN_LAT(t *testing.T) { } func TestDecodeString_DF17_ODD_LAT(t *testing.T) { - var timeStamp = time.Now() frame, err := DecodeString("8D75804B580FF6B283EB7A157117", time.Now()) @@ -81,7 +80,7 @@ func TestDecodeString_DF17_ODD_LAT(t *testing.T) { return } - if "NORMAL" != frame.mode { + if frame.mode != "NORMAL" { t.Errorf("Exported mode NORMAL, Got: %s", frame.mode) } @@ -89,82 +88,41 @@ func TestDecodeString_DF17_ODD_LAT(t *testing.T) { t.Error("Expected a timestamp that was after the test started, got something else") } - if 17 != frame.downLinkFormat { + if frame.downLinkFormat != 17 { t.Errorf("Downlink format not correct. expected 17, got %d", frame.downLinkFormat) } - if 7700555 != frame.icao { + if frame.icao != 7700555 { // 0x75804B t.Errorf("Failed to decode ICAO address correctly, expected 7700555, got: %d", frame.icao) } - if 11 != frame.messageType { + if frame.messageType != 11 { t.Errorf("Expected DF Message 11 (type: %d) but got type %d", frame.MessageType(), frame.messageType) } - if 0 != frame.messageSubType { + if frame.messageSubType != 0 { t.Errorf("Got an Incorrect DF17 sub type") } - if 0 != frame.timeFlag { + if frame.timeFlag != 0 { t.Errorf("Expected time flag to not be be UTC") } - if 1 != frame.cprFlagOddEven { + if frame.cprFlagOddEven != 1 { t.Errorf("Expected the F Flag to be ODD (1) - was even instead") } - if 2175 != frame.altitude { + if frame.altitude != 2175 { t.Errorf("Incorrect altitude! expected 2175 - got: %d", frame.altitude) } - if 88385 != frame.rawLatitude { + if frame.rawLatitude != 88385 { t.Errorf("Incorrectly decoded the RAW latitude for this frame. expected 92095, got %d", frame.rawLatitude) } - if 125818 != frame.rawLongitude { + if frame.rawLongitude != 125818 { t.Errorf("Incorrectly decoded the RAW latitude for this frame. expected 39846, got %d", frame.rawLongitude) } - -} - -// this test data liberally lifted from: http://www.ccsinfo.com/forum/viewtopic.php?p=77544 -func TestGillhamDecode(t *testing.T) { - var decode = map[int32]int32{ - 2: -1000, - 6: -900, - 4: -800, - 12: -700, - 14: -600, - 10: -500, - 11: -400, - 9: -300, - 25: -200, - 27: -100, - 26: 0, - 30: 100, - 28: 200, - 20: 300, - 22: 400, - 18: 500, - 19: 600, - 17: 700, - 49: 800, - 51: 900, - 50: 1000, - 54: 1100, - 52: 1200, - 900: 46300, - 1780: 73200, - 1027: 126600, - 1025: 126700, - } - - for k, v := range decode { - test := gillhamToAltitude(k) - if v != test { - t.Errorf("Failed to decode Gillham Code %d should be %d, got %d", k, v, test) - } - } } func BenchmarkDecodeDF17Msg11(b *testing.B) { diff --git a/lib/tracker/mode_s/frame.go b/lib/tracker/mode_s/frame.go index d87e3037..98b8b4a0 100644 --- a/lib/tracker/mode_s/frame.go +++ b/lib/tracker/mode_s/frame.go @@ -664,6 +664,7 @@ func (f *Frame) AltitudeStr() string { return "" } + // return fmt.Sprintf("%d %s M=%t, Q=%t", f.altitude, f.AltitudeUnits(), f.acM, f.acQ) return fmt.Sprintf("%d %s", f.altitude, f.AltitudeUnits()) }