PyLib in Your Own Applications
The ahoi PyLib is a python library intended to be used in your own applications. This tutorial will show you the basic concepts and functions.
Note
The PyLib has to be installed on every device, on which your PyLib-based programs run.
Modem Wrapper Class
The PyLib contains a helpful wrapper class Modem that allows easy access and control of an ahoi modem via Python.
It is included via
from ahoi.modem.modem import Modem
You create an instance of type Modem
myModem = Modem()
Connecting to a Modem
You connect to a modem via a serial port or TCP connection
myModem.connect("connection")
"connection" is either
- the name of a serial device (e.g.,
/dev/ttyUSB0orCOM1) or - the IP address (and port number) of a serial forwarder with prefix
"tcp@"(e.g.,"tcp@192.168.1.50:2464")
To close the connection to a modem, use
myModem.close()
Sending a Data Packet
To let the modem send a data packet on the acoustic channel, use
myModem.send(src, dst, type, payload=bytearray(), status=None, dsn=None)
Todo
Do we actually use "src"?
| Parameter | Description |
|---|---|
src |
ID of the source modem (8 bits). Typical the id the modem which send the packet. |
dst |
ID of the modem which should receive the packet or 255 for broadcasting |
payload |
data to send |
status |
bit field of status flags |
dsn |
sequence number, when 'None' its automatic handled |
Refer to the ahoi Packet Format for details.
Example:
myModem.send(dst=0xFF, payload=bytearray(8), status=0, dsn=10)
Receiving a Data Packet
Reception of data and command packets is realized by registering packet handler callback methods for a Modem instance.
Upon packet reception, the callback methods are called and the received packet is handed over to the callback methods as parameter.
Callback Methods
Callback methods have the following signature:
def rxCallback(pkt):
...
Modem with name myModem via
myModem.addRxCallback(rxCallback)
myModem.removeRxCallback(rxCallback)
The registration of multiple callback methods (with unique names) is supported. Note that for each received packet, all of the installed callback methods are called.
Starting Reception
Reception must be activated for a modem by calling the method receive() in either blocking (thread = False) or non-blocking (thread = True) mode.
myModem.receive(thread)
In blocking mode, the method receive() runs in an endless loop and only returns, if the connection to the modem is interrupted.
In non-blocking mode, the method receive() starts a thread, which takes care of reception, and returns directly afterwards.
The thread is automatically exited, when the connection to the modem is closed or interrupted.
Modem Configuration
To interact with a modem other than sending and receiving acoustic data packets, individual wrapper methods are available.
Examples are to query the firmware version or get/set a modem ID.
We kindly refer you to modem.py for a complete list of available methods.
Distance Measurements / Ranging
The ahoi modem supports distance measurements (also called ranging) by means of two-way time-of-flight (TWR) estimation (between any pair of modems).
A distance measurement is initiated by sending a data packet with R-flag (cf. ahoi Packet Format) from one modem to another modem or as a broadcast.
As such, you can trigger a distance measurement from PyLib via status = 2 when sending a data packet.
When the distance measurement is complete, the sending modem will forward the received ACK to the host.
This ACK contains half the cumulated time-of-flight of the data packet and the ACK and can be processed by a packet handler.
Note that you can filter out ACKs with ranging information by its packet type 0x7F and a non-zero payload length.
The payload can be converted to the distance by the following calculation:
SPEED_OF_SOUND = 1500 # in meters per second
def rxCallback(pkt):
# check if we have received a ranging ack
if pkt.header.type == 0x7F and pkt.header.length > 0:
tof = 0
for i in range(0,4):
tof = tof * 256 + pkt.payload[i]
print("distance: %6.1f" % (tof * 1e-6 * SPEED_OF_SOUND))
...
Underwater Speed of Sound
The value for the speed of sound depends on the medium and temperature. Look up the value in a table, or measure the value on your own.
If a modem receives a data packet with R-flag, it responds with a ranging request, if and only if the field dst of the received packet
- equals the receiver's ID or
- is the broadcast address.
Using the broadcast address in the dst field of such a packet may, hence, trigger multiple simultaneous ACKs from different receiving modems.
This is undesired, because the sender cannot receive overlapping ACKs.
For this particular purpose, the ahoi modem supports delayed ACKs: By configuring a different (and large enough) delay for each modem in a network, multiple ACKs with distance information can be collected by sending a single data packet with R-flag.
To configure such a delay for an instance myModem of type Modem, use
myModem.rangeDelay(delay)
delay is an integer value in milliseconds.
Effect of Ranging Delay
- The ranging delay is only effective for ACKs sent in response to a packet with R-flag. Regular ACKs (A-flag only) will always be sent immediately.
- The ranging delay is purged (subtracted) from the time-of-flight contained in a ranging ACK automatically.
Firmware Installation (Experimental)
Serial Port Only
The firmware update is only supported via a direct serial port communication. An update over ethernet is currently not possible.
PyLib supports firmware installation and updating through the external program "stm32flash", which is also used in the MoSh Firmware Installation. A firmware installation via PyLib is done with the following command:
myModem.program(img='ahoi.hex', empty)
empty to
True, if the modem is empty (i.e., no previous firmware has been installed) orFalse, if the modem contains a previous firmware version and shall be updated.