@ -91,6 +91,53 @@ def encode_utf8(text):
return bytes ( buf )
def encode_iso8 ( text ) :
""" Encode text into a 28-byte TA buffer (format 1 / ISO-8859-1).
More widely displayed than UTF - 8 on some radios ( notably Hytera , which often
will not render format 2 / UTF - 8 Talker Alias ) . """
text = truncate_talker_alias ( text )
raw = text . encode ( ' latin-1 ' , errors = ' replace ' ) [ : 27 ]
size = len ( raw )
header = ( TA_FORMAT_ISO8 << 6 ) | ( size << 1 ) | 0
buf = bytearray ( DMRA_BUF_LEN )
buf [ 0 ] = header
buf [ 1 : 1 + size ] = raw
return bytes ( buf )
def encode_7bit ( text ) :
""" Encode text into a 28-byte TA buffer (format 0 / 7-bit packed).
Bit stream is format ( 2 ) + size ( 5 ) + 7 bits per char , MSB - first ; this is the
exact inverse of decode_ta ( ) ' s 7-bit path. For the oldest radios. " " "
text = truncate_talker_alias ( text )
chars = text . encode ( ' ascii ' , errors = ' replace ' ) [ : 31 ]
size = len ( chars )
bits = [ ( TA_FORMAT_7BIT >> 1 ) & 1 , TA_FORMAT_7BIT & 1 ]
for b in range ( 4 , - 1 , - 1 ) :
bits . append ( ( size >> b ) & 1 )
for ch in chars :
for b in range ( 6 , - 1 , - 1 ) :
bits . append ( ( ch >> b ) & 1 )
total = DMRA_BUF_LEN * 8
bits = ( bits + [ 0 ] * total ) [ : total ]
buf = bytearray ( DMRA_BUF_LEN )
for i , bit in enumerate ( bits ) :
if bit :
buf [ i / / 8 ] | = 1 << ( 7 - ( i % 8 ) )
return bytes ( buf )
_TA_ENCODERS = { ' utf8 ' : encode_utf8 , ' iso8 ' : encode_iso8 , ' 7bit ' : encode_7bit }
def encode_ta_buffer ( text , text_format = ' utf8 ' ) :
""" Encode text into a 28-byte TA buffer in the requested format.
text_format : ' utf8 ' ( default ) , ' iso8 ' ( ISO - 8859 - 1 , best for Hytera ) , ' 7bit ' . """
return _TA_ENCODERS . get ( text_format , encode_utf8 ) ( text )
def blocks_from_buffer ( buf ) :
""" Split a 28-byte encoded buffer into four 7-byte DMRA payloads. """
buf = buf . ljust ( DMRA_BUF_LEN , b ' \x00 ' ) [ : DMRA_BUF_LEN ]
@ -121,10 +168,10 @@ def buffer_from_blocks(blocks):
# Standalone DMRA packet builders / parser
# ---------------------------------------------------------------------------
def build_dmra_packets ( rf_src , text ):
def build_dmra_packets ( rf_src , text , text_format = ' utf8 ' ):
""" Build the 1-4 HBP DMRA packets needed to carry ' text ' (server injection). """
rf = rf_src [ : 3 ] if len ( rf_src ) > = 3 else rf_src . ljust ( 3 , b ' \x00 ' ) [ : 3 ]
encoded = encode_ utf8( tex t)
encoded = encode_ ta_buffer( text , text_forma t)
blocks = blocks_from_buffer ( encoded )
count = required_ta_block_count ( encoded )
packets = [ ]
@ -199,9 +246,9 @@ def _encode_emblc(_lc):
return { 1 : out [ 0 : 32 ] , 2 : out [ 32 : 64 ] , 3 : out [ 64 : 96 ] , 4 : out [ 96 : 128 ] }
def encode_talker_alias_emblc ( text ):
def encode_talker_alias_emblc ( text , text_format = ' utf8 ' ):
""" Embedded-LC dicts for TA blocks 0..N-1 plus block count N (1-4). """
encoded = encode_ utf8( tex t)
encoded = encode_ ta_buffer( text , text_forma t)
blocks = blocks_from_buffer ( encoded )
count = required_ta_block_count ( encoded )
emblcs = [ _encode_emblc ( talker_alias_lc_bytes ( i , blocks [ i ] ) ) for i in range ( count ) ]
@ -263,7 +310,13 @@ def ta_settings(CONFIG, system_name=None):
fmt = sys_cfg . get ( ' TALKER_ALIAS_FORMAT ' )
if fmt is None :
fmt = g . get ( ' TALKER_ALIAS_FORMAT ' , ' {callsign} {fname} ' )
return { ' enabled ' : bool ( enabled ) , ' mode ' : mode , ' format ' : str ( fmt ) }
tfmt = sys_cfg . get ( ' TALKER_ALIAS_TEXT_FORMAT ' )
if tfmt is None :
tfmt = g . get ( ' TALKER_ALIAS_TEXT_FORMAT ' , ' utf8 ' )
tfmt = str ( tfmt ) . lower ( )
if tfmt not in ( ' utf8 ' , ' iso8 ' , ' 7bit ' ) :
tfmt = ' utf8 '
return { ' enabled ' : bool ( enabled ) , ' mode ' : mode , ' format ' : str ( fmt ) , ' text_format ' : tfmt }
def ta_enabled ( CONFIG , system_name = None ) :
@ -351,13 +404,13 @@ def resolve_ta(CONFIG, system_name, rf_src, blocks):
' text ' : decode_ta ( buffer_from_blocks ( blocks ) ) }
if mode == ' inject ' :
return { ' source ' : ' inject ' , ' blocks ' : None ,
' text ' : format_ta_text ( CONFIG , rf_src ) }
' text ' : format_ta_text ( CONFIG , rf_src ) , ' text_format ' : settings [ ' text_format ' ] }
# both
if have_passthrough :
return { ' source ' : ' passthrough ' , ' blocks ' : dict ( blocks ) ,
' text ' : decode_ta ( buffer_from_blocks ( blocks ) ) }
return { ' source ' : ' inject ' , ' blocks ' : None ,
' text ' : format_ta_text ( CONFIG , rf_src ) }
' text ' : format_ta_text ( CONFIG , rf_src ) , ' text_format ' : settings [ ' text_format ' ] }
def ta_dmra_packets ( rf_src , resolved ) :
@ -366,7 +419,7 @@ def ta_dmra_packets(rf_src, resolved):
return [ ]
if resolved [ ' source ' ] == ' passthrough ' :
return passthrough_packets_from_blocks ( rf_src , resolved [ ' blocks ' ] )
return build_dmra_packets ( rf_src , resolved [ ' text ' ] )
return build_dmra_packets ( rf_src , resolved [ ' text ' ] , resolved . get ( ' text_format ' , ' utf8 ' ) )
def ta_emblc ( resolved ) :
@ -375,7 +428,7 @@ def ta_emblc(resolved):
return None
if resolved [ ' source ' ] == ' passthrough ' :
return encode_talker_alias_emblc_from_blocks ( resolved [ ' blocks ' ] )
return encode_talker_alias_emblc ( resolved [ ' text ' ] )
return encode_talker_alias_emblc ( resolved [ ' text ' ] , resolved . get ( ' text_format ' , ' utf8 ' ) )
# ---------------------------------------------------------------------------