diff --git a/pyModeS/c_common.pxd b/pyModeS/c_common.pxd index d45fab7..2f39078 100644 --- a/pyModeS/c_common.pxd +++ b/pyModeS/c_common.pxd @@ -17,7 +17,10 @@ cpdef bint is_icao_assigned(str icao) cpdef int typecode(str msg) cpdef int cprNL(double lat) + cpdef str idcode(str msg) +cpdef str squawk(str binstr) + cpdef int altcode(str msg) cpdef int altitude(str binstr) diff --git a/pyModeS/c_common.pyx b/pyModeS/c_common.pyx index f6a0f6d..e774586 100644 --- a/pyModeS/c_common.pyx +++ b/pyModeS/c_common.pyx @@ -160,17 +160,7 @@ cpdef long floor(double x): return c_floor(x) cpdef str icao(str msg): - """Calculate the ICAO address from an Mode-S message. - - Applicable only with DF4, DF5, DF20, DF21 messages. - - Args: - msg (String): 28 bytes hexadecimal message string - - Returns: - String: ICAO address in 6 bytes hexadecimal string - - """ + """Calculate the ICAO address from an Mode-S message.""" cdef unsigned char DF = df(msg) cdef long c0, c1 @@ -217,14 +207,7 @@ cpdef bint is_icao_assigned(str icao): @cython.boundscheck(False) @cython.wraparound(False) cpdef int typecode(str msg): - """Type code of ADS-B message - - Args: - msg (string): 28 bytes hexadecimal message string - - Returns: - int: type code number - """ + """Type code of ADS-B message""" if df(msg) not in (17, 18): return -1 # return None @@ -253,39 +236,41 @@ cpdef int cprNL(double lat): @cython.boundscheck(False) @cython.wraparound(False) cpdef str idcode(str msg): - """Compute identity (squawk code). - - Applicable only for DF5 or DF21 messages, bit 20-32. - - Args: - msg (String): 28 bytes hexadecimal message string - - Returns: - string: squawk code - - """ + """Compute identity (squawk code).""" if df(msg) not in [5, 21]: raise RuntimeError("Message must be Downlink Format 5 or 21.") - cdef bytearray _mbin = bytearray(hex2bin(msg).encode()) + squawk_code = squawk(hex2bin(msg)[19:32]) + return squawk_code + + +@cython.boundscheck(False) +@cython.wraparound(False) +cpdef str squawk(str binstr): + """Compute identity (squawk code).""" + + if len(binstr) != 13 or set(binstr) != set('01'): + raise RuntimeError("Input must be 13 bits binary string") + + cdef bytearray _mbin = bytearray(binstr.encode()) cdef unsigned char[:] mbin = _mbin cdef bytearray _idcode = bytearray(4) cdef unsigned char[:] idcode = _idcode - cdef unsigned char C1 = mbin[19] - cdef unsigned char A1 = mbin[20] - cdef unsigned char C2 = mbin[21] - cdef unsigned char A2 = mbin[22] - cdef unsigned char C4 = mbin[23] - cdef unsigned char A4 = mbin[24] - # X = mbin[25] - cdef unsigned char B1 = mbin[26] - cdef unsigned char D1 = mbin[27] - cdef unsigned char B2 = mbin[28] - cdef unsigned char D2 = mbin[29] - cdef unsigned char B4 = mbin[30] - cdef unsigned char D4 = mbin[31] + cdef unsigned char C1 = mbin[0] + cdef unsigned char A1 = mbin[1] + cdef unsigned char C2 = mbin[2] + cdef unsigned char A2 = mbin[3] + cdef unsigned char C4 = mbin[4] + cdef unsigned char A4 = mbin[5] + # X = mbin[6] + cdef unsigned char B1 = mbin[7] + cdef unsigned char D1 = mbin[8] + cdef unsigned char B2 = mbin[9] + cdef unsigned char D2 = mbin[10] + cdef unsigned char B4 = mbin[11] + cdef unsigned char D4 = mbin[12] idcode[0] = int_to_char((char_to_int(A4)*2 + char_to_int(A2))*2 + char_to_int(A1)) idcode[1] = int_to_char((char_to_int(B4)*2 + char_to_int(B2))*2 + char_to_int(B1)) @@ -294,29 +279,15 @@ cpdef str idcode(str msg): return _idcode.decode() - #return str(byte1) + str(byte2) + str(byte3) + str(byte4) - @cython.boundscheck(False) @cython.wraparound(False) cpdef int altcode(str msg): - """Compute the altitude. - - Applicable only for DF4 or DF20 message, bit 20-32. - credit: @fbyrkjeland - - Args: - msg (String): 28 bytes hexadecimal message string - - Returns: - int: altitude in ft - - """ + """Compute the altitude.""" if df(msg) not in [0, 4, 16, 20]: raise RuntimeError("Message must be Downlink Format 0, 4, 16, or 20.") alt = altitude(hex2bin(msg)[19:32]) - return alt @@ -360,7 +331,6 @@ cpdef int altitude(str binstr): graybytes[0] = mbin[10] graybytes[7] = mbin[11] graybytes[1] = mbin[12] - # graybytes = D2 + D4 + A1 + A2 + A4 + B1 + B2 + B4 + C1 + C2 + C4 alt = gray2alt(_graybytes.decode()) @@ -409,15 +379,7 @@ cpdef str data(str msg): cpdef bint allzeros(str msg): - """Check if the data bits are all zeros. - - Args: - msg (String): 28 bytes hexadecimal message string - - Returns: - bool: True or False - - """ + """Check if the data bits are all zeros.""" d = hex2bin(data(msg)) if bin2int(d) > 0: diff --git a/pyModeS/py_common.py b/pyModeS/py_common.py index 67a4aee..719f8a6 100644 --- a/pyModeS/py_common.py +++ b/pyModeS/py_common.py @@ -206,9 +206,7 @@ def cprNL(lat: float) -> int: def idcode(msg: str) -> str: - """Compute identity (squawk code). - - Applicable only for DF5 or DF21 messages, bit 20-32. + """Compute identity code (squawk) encoded in DF5 or DF21 message. Args: msg (String): 28 bytes hexadecimal message string @@ -221,20 +219,37 @@ def idcode(msg: str) -> str: raise RuntimeError("Message must be Downlink Format 5 or 21.") mbin = hex2bin(msg) + idcodebin = mbin[19:32] - C1 = mbin[19] - A1 = mbin[20] - C2 = mbin[21] - A2 = mbin[22] - C4 = mbin[23] - A4 = mbin[24] - # X = mbin[25] - B1 = mbin[26] - D1 = mbin[27] - B2 = mbin[28] - D2 = mbin[29] - B4 = mbin[30] - D4 = mbin[31] + return squawk(idcodebin) + + +def squawk(binstr: str) -> str: + """Decode 13 bits identity (squawk) code. + + Args: + binstr (String): 13 bits binary string + + Returns: + int: altitude in ft + + """ + if len(binstr) != 13 or set(binstr) != set("01"): + raise RuntimeError("Input must be 13 bits binary string") + + C1 = binstr[0] + A1 = binstr[1] + C2 = binstr[2] + A2 = binstr[3] + C4 = binstr[4] + A4 = binstr[5] + # X = binstr[6] + B1 = binstr[7] + D1 = binstr[8] + B2 = binstr[9] + D2 = binstr[10] + B4 = binstr[11] + D4 = binstr[12] byte1 = int(A4 + A2 + A1, 2) byte2 = int(B4 + B2 + B1, 2) @@ -245,9 +260,7 @@ def idcode(msg: str) -> str: def altcode(msg: str) -> Optional[int]: - """Compute the altitude of DF4 or DF20. - - Applicable only for DF4 or DF20 message, bit 20-32. + """Compute altitude encoded in DF4 or DF20 message. Args: msg (String): 28 bytes hexadecimal message string @@ -256,7 +269,6 @@ def altcode(msg: str) -> Optional[int]: int: altitude in ft """ - alt: Optional[int] if df(msg) not in [0, 4, 16, 20]: @@ -272,7 +284,7 @@ def altcode(msg: str) -> Optional[int]: return alt -def altitude(binstr): +def altitude(binstr: str) -> Optional[int]: """Decode 13 bits altitude code. Args: @@ -282,6 +294,7 @@ def altitude(binstr): int: altitude in ft """ + alt: Optional[int] if len(binstr) != 13 or set(binstr) != set("01"): raise RuntimeError("Input must be 13 bits binary string")