Skip to content

Commit

Permalink
correct decoding of 13 bit altitude fields when reporting in 100ft in…
Browse files Browse the repository at this point in the history
…crements
  • Loading branch information
bluntelk committed Dec 1, 2023
1 parent 09cef6f commit aa57692
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 127 deletions.
43 changes: 11 additions & 32 deletions lib/tracker/mode_s/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,49 +3,28 @@ 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))

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 {
Expand Down
72 changes: 29 additions & 43 deletions lib/tracker/mode_s/decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -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...
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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()
// }
//}
62 changes: 10 additions & 52 deletions lib/tracker/mode_s/decode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand All @@ -81,90 +80,49 @@ 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)
}

if frame.timeStamp.Before(timeStamp) {
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) {
Expand Down
1 change: 1 addition & 0 deletions lib/tracker/mode_s/frame.go
Original file line number Diff line number Diff line change
Expand Up @@ -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())
}

Expand Down

0 comments on commit aa57692

Please sign in to comment.