sjlongland / aioax25 Goto Github PK
View Code? Open in Web Editor NEWAsynchronous AX.25 library using asyncio
License: GNU General Public License v2.0
Asynchronous AX.25 library using asyncio
License: GNU General Public License v2.0
I'm struggling with trying to get aioax25 to send me frames from a kiss interface. The README mentions that you can set
listen_destinations in the APRSInterface to specify which callsign/ssid to look for. Yet, when passing in listen_destinations dict to APRSInterface I get a failure.
File "/Users/i530566/devel/mine/aprsd/aprsd/kissclient.py", line 108, in setup
self.aprsint = APRSInterface(
TypeError: __init__() got an unexpected keyword argument 'listen_destinations'
I grepped the repo
└─> fgrep -rni listen_destination *
README.md:173: * `listen_destinations` is a list of AX.25 destinations. Behind the scenes,
README.md:177: the same scheme as `listen_destinations`.
I am also calling bind() on the APRSInterface and passing in my callsign and setting regex to false (assuming this means look for an exact match?), but my callback is not getting called when I send an APRS packet.
My callsign is WB4BOR-12 for this instance of aioax25 connected to direwolf. I am using a kenwood TH-D74 to send an aprs message to WB4BOR-12. You can see from the log below that aioax25 gets it, says the frame is for us, but yet then never sends it on to my registered callback from the aprsinterface.bind() call.
[08/31/2021 01:44:13 PM] [KISSRX_MSG ] [DEBUG] Processing incoming message WB4BOR*>APK004,WIDE1-1,WIDE2-1: PID=0xf0 Payload=b':WB4BOR-12:ping{93' (type APRSMessageFrame) - [/Users/i530566/devel/mine/aprsd/.venv/lib/python3.8/site-packages/aioax25/aprs/aprs.py:247]
[08/31/2021 01:44:13 PM] [KISSRX_MSG ] [DEBUG] Addressee? WB4BOR-12 - [/Users/i530566/devel/mine/aprsd/.venv/lib/python3.8/site-packages/aioax25/aprs/aprs.py:249]
[08/31/2021 01:44:13 PM] [KISSRX_MSG ] [DEBUG] Frame is for us! - [/Users/i530566/devel/mine/aprsd/.venv/lib/python3.8/site-packages/aioax25/aprs/aprs.py:261]
[08/31/2021 01:44:13 PM] [KISSRX_MSG ] [DEBUG] This is a real message for us! - [/Users/i530566/devel/mine/aprsd/.venv/lib/python3.8/site-packages/aioax25/aprs/aprs.py:276]
[08/31/2021 01:44:13 PM] [KISSRX_MSG ] [DEBUG] Handling incoming frame WB4BOR*>APK004,WIDE1-1,WIDE2-1: PID=0xf0 Payload=b':WB4BOR-12:ping{93' - [/Users/i530566/devel/mine/aprsd/.venv/lib/python3.8/site-packages/aioax25/router.py:98]
[08/31/2021 01:44:13 PM] [KISSRX_MSG ] [DEBUG] Dest: WB4BOR-12 callsign: WB4BOR - [/Users/i530566/devel/mine/aprsd/.venv/lib/python3.8/site-packages/aioax25/router.py:108]
[08/31/2021 01:44:13 PM] [KISSRX_MSG ] [DEBUG] RXers = dict_values([]) - [/Users/i530566/devel/mine/aprsd/.venv/lib/python3.8/site-packages/aioax25/router.py:123]
[08/31/2021 01:44:13 PM] [KISSRX_MSG ] [DEBUG] Dispatching frame to 0 receivers - [/Users/i530566/devel/mine/aprsd/.venv/lib/python3.8/site-packages/aioax25/router.py:139]
I am seeing a problem with sending a message that includes a message number. It looks like the message number is being stripped off the packet, resulting in incorrect acks coming back.
Here is an example from aprsd.
08/02/2022 12:43:33 PM TXPKT-4-ping INFO messaging.py:584
Sending Message _______________(TX:1)
Raw : WB4BOR-12>APZ100::WB4BOR-11:ping{4
To : WB4BOR-11
Message : ping
Msg # : 4
Sending Message _______________ Complete
08/02/2022 12:43:33 PM TXPKT-4-ping DEBUG GET client tcpkiss client.py:226
08/02/2022 12:43:34 PM TXPKT-4-ping DEBUG Send WB4BOR-12>APZ100::WB4BOR-11:ping{4 kiss.py:108
TO KISS
08/02/2022 12:43:34 PM TXPKT-4-ping INFO Send one-shot to WB4BOR-11: ping aprs.py:160
08/02/2022 12:43:34 PM TXPKT-4-ping INFO Sending WB4BOR-12>WB4BOR-11,WIDE1-1,WIDE2-1: PID=0xf0 Payload=b':WB4BOR-11:ping' aprs.py:330
08/02/2022 12:43:34 PM TXPKT-4-ping DEBUG Adding to queue: WB4BOR-12>WB4BOR-11,WIDE1-1,WIDE2-1: PID=0xf0 interface.py:62
Payload=b':WB4BOR-11:ping'
08/02/2022 12:43:34 PM TXPKT-4-ping DEBUG Scheduling next transmission ASAP interface.py:113
08/02/2022 12:43:34 PM RX_MSG DEBUG Transmitting WB4BOR-12>WB4BOR-11,WIDE1-1,WIDE2-1: PID=0xf0 interface.py:141
Payload=b':WB4BOR-11:ping'
08/02/2022 12:43:34 PM RX_MSG DEBUG XMIT AX.25 WB4BOR-12>WB4BOR-11,WIDE1-1,WIDE2-1: PID=0xf0 kiss.py:619
Payload=b':WB4BOR-11:ping'
08/02/2022 12:43:34 PM RX_MSG DEBUG XMIT FRAME '00ae8468849ea4f6ae8468849ea478ae92888a624062ae92888a64406303f03a57423 kiss.py:226
4424f522d31313a70696e67'
The direwolf log
[0L] WB4BOR-12>WB4BOR-11,WIDE1-1,WIDE2-1::WB4BOR-11:ping
The recieving APRSD instance view of the packet
today at 12:42:45 PM Received Message _______________
today at 12:42:45 PM Raw : WB4BOR-12>WB4BOR-11,K4CQ-4,WIDE1*,WIDE2-1,qAR,APPOMX::WB4BOR-11:ping
today at 12:42:45 PM From : WB4BOR-12
today at 12:42:45 PM Message : ping
today at 12:42:45 PM Msg # : 0
today at 12:42:45 PM Received Message _______________ Complete
today at 12:42:45 PM08/02/2022 12:42:45 PM RXPKT-WB4BOR-12> DEBUG Send ACK(WB4BOR-12:0) to radio.
As you can see the original message was sent to tcpkiss with a message number of 4, so I can get acks for message 4 back from the destination.
The destination didn't get a message number as part of the packet as it was stripped out, so it sent an ack with message number of 0.
If there are official digipeaters in the area, it is undesirable to try and relay on their behalf.
Moreover, sending immediately risks doubling, which leads to packet loss.
Wait a randomised delay before digipeating a message. If we hear a downlink frame for that same message, discard and cancel our scheduled transmission.
Kenwood in their TH-F7E for unknown reasons appends a carriage return in their message ID.
Xastir, appends a }
, which may be down to the ack-reply in newer variants of APRS.
I managed to kludge my way around what Kenwood is doing, but Xastir down-right confuses aioax25
. This information needs to be stripped.
In the file frame.py, SABM and SABME classes have their modifiers swapped. The correct values should be :-
Class AX25SetAsyncBalancedModeExtendedFrame should have a modifier of MODIFIER = 0b01101111
Class AX25SetAsyncBalancedModeFrame should have a modifier of MODIFIER = 0b00101111
It also appears that the P/F flag needs more investigation, and should possibly be set. The basis for this is that my PicoPacket TNC with 1996 firmware has the P/F bit set. In my case, the picopacket is using 0x3f as the value for SABM.
I suspect that existing code uses a mask to ignore the p/f bit.
Following your code, you could ADD these MODIFIER = 0b01101111 and MODIFIER = 0b00101111 respectively for SABME and SABM.
hey thanks for the update for version 0.0.11.
I'm seeing some weird behavior at times. I am connecting to direwolf over tcpkiss and there are times
when I send a message to aioax25, and I the debugging output shows that the packet is added to the send queue.
Then it never goes out
When I then send a message to direwolf from my radio, kiss gets that message and then sends it to aprsd, and then I see
the queued up messages in aioax25 go out to direwolf and then out to RF finally.
Is there some reason you can think of that the messages get stuck in the queue and don't go to direwolf?
09/25/2022 08:09:42 PM TXPKT-3-hey f DEBUG Send 'b':WB4BOR :hey from webchat{3'' TO KISS kiss.py:147
09/25/2022 08:09:42 PM TXPKT-3-hey f INFO Sending WB4BOR-12>WB4BOR,WIDE1-1,WIDE2-1: PID=0xf0 Payload=b':WB4BOR :hey from webchat{3' aprs.py:330
09/25/2022 08:09:42 PM TXPKT-3-hey f DEBUG Adding to queue: WB4BOR-12>WB4BOR,WIDE1-1,WIDE2-1: PID=0xf0 Payload=b':WB4BOR :hey from interface.py:62
webchat{3'
then 2 minutes later since aprsd didn't get an ack, it sends it again. It doesn't actually go out to direwolf and RF until direwolf gets a packet from RF to kiss to aprsd.
I'm trying to wrap my head around how this package works.
I'm trying to use this to connect to direwolf's TCP KISS interface to recieve aprs messages.
I haven't quite figured out how the KISSPort maps to a TCP based kissdevice, but my test script looks like this.
kissdev = kiss.TCPKISSDevice("192.168.1.7", 8001, log=LOG)
kissdev.open()
kissport0 = kissdev[0]
ax25int = interface.AX25Interface(
kissport=kissport0,
log=LOG
)
aprsint = APRSInterface(
ax25int=ax25int,
mycall='WB4BOR',
log=LOG
)
But it just just and exits. How do I get this to loop forever waiting for packets to come in?
[02/22/2021 07:57:44 PM][MainThread ][DEBUG] [/home/waboring/devel/aioax25/aioax25/kiss.py.__init__:446] Created
[02/22/2021 07:57:44 PM][MainThread ][DEBUG] [/home/waboring/devel/aioax25/aioax25/kiss.py.open:386] Opening device
[02/22/2021 07:57:44 PM][MainThread ][DEBUG] [/home/waboring/devel/aioax25/aioax25/kiss.py._open:449] Call open
[02/22/2021 07:57:44 PM][MainThread ][DEBUG] [/home/waboring/devel/aioax25/aioax25/kiss.py._open:455] Call open Done
[02/22/2021 07:57:44 PM][MainThread ][DEBUG] [/home/waboring/devel/aioax25/aioax25/kiss.py.__getitem__:373] OPEN new port 0
[02/22/2021 07:57:44 PM][MainThread ][DEBUG] [/home/waboring/devel/aioax25/aioax25/kiss.py.__getitem__:375] <aioax25.kiss.KISSPort object at 0x7f82202bbcd0>
My TCPKISSDevice class looks like this:
class TCPKISSDevice(BaseKISSDevice):
_interface = None
READ_BYTES = 1000
def __init__(self, host: str, port: int, *args, **kwargs):
super(TCPKISSDevice, self).__init__(*args, **kwargs)
self.address = (host, port)
self._log.debug('Created')
def _open(self):
self._log.debug('Call open')
self._interface = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self._interface.connect(self.address)
self._state = KISSDeviceState.OPEN
self._loop.add_reader(self._interface, self._on_recv_ready)
self._loop.call_soon(self._init_kiss)
self._log.debug('Call open Done')
def _on_recv_ready(self):
self._log.debug('Called')
try:
read_data = self._interface.recv(self.READ_BYTES, timeout=30)
self._receive(read_data)
except:
self._log.exception('Failed to read from serial device')
def _send_raw_data(self, data):
self._serial.write(data)
def _close(self):
self._loop.remove_reader(self._interface)
socket.close(self._interface)
self._interface = None
self._state = KISSDeviceState.CLOSED
In cases where digipeaters are in use, we should be acting on only the first copy of a message we see and ignoring the others.
Confirmable messages have a message ID for that exact purpose.
Hey man,
I am working on a webchat version of aprsd and am using the latest from github for aioax25. Every time I startup APRSInterface I get an exception from asyncio
Exception in callback <Task pending name='Task-1' coro=<TCPKISSDevice._open_connection() running at /Users/i530566/devel/mine/hamradio/aioax25/aioax25/kiss.py:540> wait_for=<Future pending cb=[BaseSelectorEventLoop._sock_write_done(9)(), <TaskWakeupMethWrapper object at 0x104d8a7f0>()]>>()
handle: <Handle <Task pending name='Task-1' coro=<TCPKISSDevice._open_connection() running at /Users/i530566/devel/mine/hamradio/aioax25/aioax25/kiss.py:540> wait_for=<Future pending cb=[BaseSelectorEventLoop._sock_write_done(9)(), <TaskWakeupMethWrapper object at 0x104d8a7f0>()]>>()>
Traceback (most recent call last):
File "/Users/i530566/.pyenv/versions/3.8.7/lib/python3.8/asyncio/events.py", line 81, in _run
self._context.run(self._callback, *self._args)
TypeError: '_asyncio.Task' object is not callable
Here is how I launch APRSInterface:
https://github.com/craigerl/aprsd/blob/small_refactor/aprsd/clients/kiss.py#L46-L64
Payload content: ac966884ae9260ac966884ae92fcae92888a6240e2ae92888a64406303f03a564b344257492020203a4c23342043523a65323030363030383133303830323335303630306439366120543a3232353133335a7b33387d4e6f6e65
4e6f6e65
is None
… that's a Pythonism that crept in on the packet network.
Right now, we're using APZAIO
due to the software library's experimental status, but this really should be replaced with an official allocation so people can recognise anything that's aioax25
-based.
Hey there,
Thanks for making this project. I'm trying to write a little chat app over AX.25 and was trying to catch to errors.
The first I figured out, if direwolf isn't running the device never transitons to OPEN, so I can catch that:
while self.device.state != KISSDeviceState.OPEN:
msg = f"Waiting for direwolf connection... attempt {i}/{max_attempts}"
print(msg, end="\r") # noqa: T201
if i > max_attempts:
msg = "Cannot connect to direwolf. Is it running?"
LOGGER.critical(msg)
sys.exit(1)
await asyncio.sleep(1)
i += 1
The second, which I cannot figure out, is if direwolf disappears after the device was open.
raw_frame = AX25RawFrame(
destination=dest,
source=self.callsign,
control=0,
payload=payload,
)
interface.transmit(raw_frame)
In this case if direwolf is no longer running I get the following:
Unhandled exception in event loop:
File "/usr/lib64/python3.12/asyncio/events.py", line 84, in _run
self._context.run(self._callback, *self._args)
File "/home/bthornto/github/ax25/venv/lib64/python3.12/site-packages/aioax25/kiss.py", line 321, in _send_data
self._send_raw_data(data)
File "/home/bthornto/github/ax25/venv/lib64/python3.12/site-packages/aioax25/kiss.py", line 448, in _send_raw_data
self._transport.write(data)
^^^^^^^^^^^^^^^^^^^^^
Exception 'NoneType' object has no attribute 'write'
Any suggestions or pointers on how to catch this one? I would like to gracefully exit the app when it happens.
Thanks for any info/suggestions,
Brad
Hello,
I am very close to getting aprsd working with aioax25 with direwolf. There is an odd behavior that I am seeing when getting aprs packets from aioax25, which is the sender's callsign gets a * after it, which causes aprslib to fail to decode the packet.
These lines of code are where the * is being added to the sender's callsign.
https://github.com/sjlongland/aioax25/blob/master/aioax25/frame.py#L713-L714
Here is a sample packet sent from my Kenwood TH-d74, which direwolf picks up and sends to aioax25
[DEBUG] Got an APRS Frame 'WB4BOR*>APK004,WIDE1-1,WIDE2-1: PID=0xf0 Payload=b':WB4BOR-12:ping{27'' - [/Users/i530566/devel/mine/aprsd/aprsd/threads.py:373]
[09/01/2021 02:15:27 PM] [KISSRX_MSG ] [DEBUG] Decoding WB4BOR*>APK004,WIDE1-1,WIDE2-1::WB4BOR-12:ping{27 - [/Users/i530566/devel/mine/aprsd/aprsd/threads.py:377]
The direwolf log entry for the packet
WB4BOR audio level = 8(4/2) [NONE] ___|||||_ │
[0.5] WB4BOR>APK004,WIDE1-1,WIDE2-1::WB4BOR-12:ping{27<0x0d> │
[0H] WB4BOR>APK004,APPOMX*,WIDE2-1::WB4BOR-12:ping{27<0x0d>
You can see the fail to decode here:
Exception in callback KISSRXThread.process_packet(interface=<aioax25.aprs...t 0x1040a7970>, frame=<aioax25.aprs...t 0x1040b3160>)()
handle: <Handle KISSRXThread.process_packet(interface=<aioax25.aprs...t 0x1040a7970>, frame=<aioax25.aprs...t 0x1040b3160>)()>
Traceback (most recent call last):
File "/Users/i530566/devel/mine/aprsd/.venv/lib/python3.8/site-packages/aprslib/parsing/__init__.py", line 101, in parse
parsed.update(parse_header(head))
File "/Users/i530566/devel/mine/aprsd/.venv/lib/python3.8/site-packages/aprslib/parsing/common.py", line 45, in parse_header
raise ParseError("fromcallsign is invalid")
aprslib.exceptions.ParseError: fromcallsign is invalid
I've never seen an aprs frame where the fromcallsign gets a * put after it. If I comment those lines 712 and 714 out of the frame.py, then everything works and is happy.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.