RadioController.send_tnc_data with VR-N76
I have observed a behaviour while sending an APRS message using RadioController.send_tnc_data. This method works well while working with the BTECH UV-PRO, but I am facing trouble when working with the VR-N76. I saw in the README that the package has not been tested with the VR-N76, which might be the issue.
The behaviour isn't consistent, as there are times when I can send one packet once in a while, but 95% of the time, the transmission fails. I will include the code for encoding and decoding the packet.
Encoder
def ax25_callsign(call, ssid=0, last=False):
call = call.upper().ljust(6) # Ensure call is exactly 6 characters
encoded = bytes([(ord(c) << 1) for c in call[:6]]) # Shift ASCII values
encoded += bytes([0x60 | (ssid << 1) | (0x01 if last else 0x00)]) # SSID + last bit
return encoded
def build_aprs_message(dest_call, src_call, digipeaters, recipient, message):
# Encode AX.25 callsigns
dest = ax25_callsign(dest_call, 0, last=False)
src_ssid = int(src_call.split('-')[1]) if '-' in src_call else 0
src = ax25_callsign(src_call.split('-')[0], src_ssid, last=(len(digipeaters) == 0))
# Encode digipeaters
digis = b""
for i, digi in enumerate(digipeaters):
call, ssid = digi.split('-') if '-' in digi else (digi, 0)
last = (i == len(digipeaters) - 1) # Only last digipeater has last bit set
digis += ax25_callsign(call, int(ssid), last=last)
# Construct APRS payload correctly (ensure exactly 9 characters for recipient)
recipient = recipient.ljust(9)[:9] # Left-align and enforce 9-char limit
aprs_payload = f":{recipient}:{message}" # Ensure only two colons
control_pid = b'\x03\xf0' # UI-frame, no layer 3 protocol
return dest + src + digis + control_pid + aprs_payload.encode()
Decoder
def ax25_decode(addr_bytes):
callsign = ''
for i in range(6):
char = (addr_bytes[i] >> 1) & 0x7F
if char != 0x20:
callsign += chr(char)
ssid = (addr_bytes[6] >> 1) & 0x0F
return f"{callsign}-{ssid}" if ssid else callsign
def parse_aprs(data):
source_addr = ax25_decode(data[7:14])
dest_addr = ax25_decode(data[:7])
digipeaters = []
offset = 14
while offset + 7 < len(data) and not (data[offset] & 1):
digipeaters.append(ax25_decode(data[offset:offset+7]))
offset += 7
payload_index = data.index(b'\x03\xf0') + 2
aprs_payload = data[payload_index:].decode(
'ascii', errors='ignore').strip()
aprs_packet = f"{source_addr}>{dest_addr}" + \
(f",{','.join(digipeaters)}" if digipeaters else "") + \
f":{aprs_payload}"
return aprs_packet
Usage
frame = build_aprs_message("APN000", "NOCALL-1", ["WIDE1-1", "WIDE2-2"], "DESCAL-1", "Hello there")
print(frame)
parsed = parse_aprs(frame)
print(parsed)
The above algorithm is implemented to work for both UV-PRO and VR-N76, but its only working for one.
There's a chance the VR-N76 is doing something related to this bug: https://github.com/khusmann/benlink/issues/1
Two things to try to see if this is related:
- After it fails, if you immediately try re-sending the packet, does it succeed? (by "immediately" I mean resend within 0.1s)
- If you connect to the audio channel, does it start working reliably?
And for general context:
- What is your firmware version on the N76?
- Do you get an error (e.g. "invalid state") when the transmission fails? Or does it simply not transmit?
firmware version 0.9.3 just got out of beta.