User Tools

Site Tools


lora_dynamic_phy_layer_example

GNU Radio LoRa PHY layer for MAC experimentation

This tutorial executes a LoRa transmission between a node and a gateway using docker in CorteXlab, transmitting dummy packets between them.

It shows how to use the interface of a plug and play complete LoRa dynamic physical layer, that can be found inside the following docker image or on this GitHub repository. It allows to connect any upper layer that will control the physical layer (message and parameters), and receive all the information it needs (SNR, message and noise energy levels, crc presence, coding rate, number of (corrected) errors…).

This implementation stands on the original LoRa PHY version made by EPFL.

In this tutorial, we will first deploy on CorteXlab two examples of upper layers connected to the LoRa physical layer. After showing the functioning of this simple scenario, we will then pull the docker image in order to modify it, add a basic functionnality on the upper layer and redeploy it on CorteXlab.

Example upper layer/original experiment

Docker

In this tutorial, we will use the docker image available on Dockerhub, which includes the CorteXlab toolchain, alongside a GitHub repository providing the LoRa physical layer properly configured and an upper layer example.

Thanks to a docker image, it is possible to easily run on your local machine an environment suitable for CorteXlab, with gnuradio and needed software. Once your project is ready, you can use your upgraded docker image in the testbed. This will allow you to quickly transfer all of your project, files and environment, as in your local computer but to be used within the testbed on SDR nodes.

Create the scenario

In order to deploy this suitable environment, you will have to create the following files and folders.

For the following, the task will refer to the instructions given by Airlock, the scenario file and minus. The experiment will refer to the LoRa transmissions.

Create on your machine a folder task_lora and in it a file scenario.yaml which will indicate the nodes to use and how to use them. It should look like this:

# Example scenario description file
#
#   All lines starting with "#" and empty lines are ignored
#
#
# Scenario textual description
#   simple string (a one liner)
description: Lora Dynamic Physical Layer - Docker and Cortexlab
#
# Experiment maximum duration
#   Time after which the experiment is forced to stop
#   integer (seconds)
duration: 600
#
# Node list
#
#   format:
#
#   nodes:
#     (machine):
#       command: (entry point script relative to the task root)

nodes:
  node14:
    container:
    - image: amauryparis/cxlb_lora:master
      command: /usr/sbin/sshd -p 2222 -D

  node16:
    container:
    - image: amauryparis/cxlb_lora:master
      command: /usr/sbin/sshd -p 2222 -D

This scenario runs the given docker image on nodes 14 and 16, and opens the containers to a ssh connection.

If you want to modify this scenario, for example in order to automatize the experiment, check out the first tutorial on how to use docker with CorteXlab here. It explains the functioning of the scenario.yaml file and the other possibilities it offers.

For more info on where these nodes are located inside the platform, please check the node position map at the home of this wiki here.

Assuming your account has been correctly created, you can now copy the folder with the scenario file into the Airlock SSH front-end:

you@yourpc:~$ scp -P 2269 -v [-i path/to/your/key] [-r] path/to/local/file/task_lora username@gw.cortexlab.fr:/cortexlab/homes/[YOUR CORTEXLAB USERNAME]/workspace/

Access Airlock

You can now access the Airlock SSh server that will allow you to manage your task.

you@yourpc:~$ ssh -X -v [-i path/to/the/key] username@gw.cortexlab.fr

Create the task file

On airlock, before submitting the task to the nodes, we need first to put the task into a format that can be readily understood by Minus. See here for more information on minus and how to manage tasks.

you@srvairlock:~/ cd /cortexlab/homes/[YOUR CORTEXLAB USERNAME]/workspace/
you@srvairlock:~/workspace$ minus task create task_lora
you@srvairlock:~/workspace$ ls
task_lora  task_lora.task

Submit the task

Now we need to give the task to Minus, so that it can operate its magic.

First we need to reserve the CorteXlab room:

you@srvairlock:~/workspace$ oarsub -l nodes=BEST,walltime=0:30:00 -I

(If you're running your reservation in a container reservation):

you@srvairlock:~/workspace$ oarsub -t inner=<id of container> -l {"network_address in ('mnode4.cortexlab.fr', 'mnode6.cortexlab.fr')"}/nodes=2,walltime=0:30:00 -I

(Be sure that no one else is using the same node as you)

This will run a 30 minute job and open a sub-shell in which you can run minus tasks. This sub-shell will be killed after 30 minutes, and if you leave the shell earlier, it will terminate the corresponding oar job. More documentation on oar can be found here. You can also monitor the current jobs in the gantt web interface.

We then submit the minus task:

you@srvairlock:~/workspace$ minus task submit task_lora.task
Task with id 15 enqueued user <login>.

Run the experiment

Setup

Now the node we want to use are up and running with the docker image we set up earlier. We can access them with ssh to run our experiment. To do so, open a new terminal per node you want to use, here two, access Airlock and run the following command, here for node 14:

you@srvairlock:~/workspace$ ssh -p 2222 root@mnode14

You may need to wait a few seconds so that the task is running before being able to connect to the nodes.

You are now connected to the node with the docker environment you set up earlier.

In order to run this example you should launch 4 terminals: 2 for each USRP. Therefore, use the same ssh command to connect to the node 14 or 16 on each of the 3 remaining terminals.

The part we are interested in can be found in /root/LoRa_PHY_Cxlb.git, and more specifically in the gr-lora_sdr/apps sub-folder.

root@mnode14:~/LoRa_PHY_Cxlb.git/gr-lora_sdr/apps# ls -1
CMakeLists.txt
'Hier blocks'
LoRa_node.grc  
README.txt  
lora_dyn_node.py
setpaths.sh
udp_BS.py
udp_Node.py

Let's go over each file in this folder:

  • CMakeLists.txt: build file
  • Hier blocks: folder containing the .grc files for the hierarchical blocks of the LoRa receiver and transmitter
  • setpaths.sh: script that add the library and python paths for the current shell process. Already executed during the creation of the docker container.

LoRa_node.grc and the corresponding python file lora_dyn_node.py contains a LoRa physical layer connected to a dynamic udp JSON interface.

In order to show its utility, two python scripts are provided:

  • udp_Node.py: upper layer example for a node, connected to the lora_dyn_node script through its udp JSON interface. Sends the message given by the user
  • udp_BS.py: upper layer example for a base station, connected to the lora_dyn_node script through its udp JSON interface. Sends an acknoledgment.
    :warning: Both udp_BS.py and udp_Node.py have to be run with python3, whereas lora_dyn_node has to be run with python2.

Open two terminals on each of the two nodes through SSH. The cortexlab node 14 will be used as the LoRa node and the cortexlab node 16 as the base station. Enter the following commands on the specified terminal to begin the experiment:

# From node 14 - on the 1st terminal, refered to as NODE.phy
root@mnode14:~/LoRa_PHY_Cxlb.git/gr-lora_sdr/apps# python2 lora_dyn_node.py
# From node 14 - on the 2nd terminal, refered to as NODE.up
root@mnode14:~/LoRa_PHY_Cxlb.git/gr-lora_sdr/apps# python3 udp_Node.py
# From node 16 - on the 3rd terminal, refered to as BS.phy
root@mnode16:~/LoRa_PHY_Cxlb.git/gr-lora_sdr/apps# python2 lora_dyn_node.py
# From node 16 - on the 4th terminal, refered to as BS.up
root@mnode16:~/LoRa_PHY_Cxlb.git/gr-lora_sdr/apps# python3 udp_BS.py

Send and receive messages

On the NODE.up terminal and the BS.up one, first choose your UDP TX and RX port. On CorteXlab use the default ports (6788 and 6790) for both the base station and the node will work since they are connected to different computers.

Then enter one of the following keywords to change the corresponding parameter if needed on the NODE.up terminal.

CR - Coding Rate
SF - Spreading Factor
GTX - Gain for TX chain
GRX - Gain for RX chain
FTX - USRP frequency for TX chain
FRX - USRP frequency for RX chain
MSG - Data to transmit

Enter the new value, then type send to send this new parameter to the physical layer. If a new MSG value is sent to the physical layer it is transmitted to the base station, which should respond with an acknowledgment.

For example, try running the following commands on terminal NODE.up in order to modify the RX gain of the node:

    Enter the parameter OR "send" to send all stored commands OR "print" to display the current parameters: print

    Enter the parameter OR "send" to send all stored commands OR "print" to display the current parameters: GRX
    Enter the new value of the Gain for RX chain :15
    Command added to list : {'GRX': '15'}

    Enter the parameter OR "send" to send all stored commands OR "print" to display the current parameters: send
    Enter the parameter OR "send" to send all stored commands OR "print" to display the current parameters: print

Check out the results on terminal NODE.phy:

    root@mnode14:~/LoRa_PHY_Cxlb.git/gr-lora_sdr/apps# python2 lora_dyn_node.py 
    [INFO] [UHD] linux; GNU C++ version 8.3.0; Boost_106700; UHD_3.15.0.0-124-geb448043
    [INFO] [USRP2] Opening a USRP2/N-Series device...
    [INFO] [USRP2] Current recv frame size: 1472 bytes
    [INFO] [USRP2] Current send frame size: 1472 bytes
    [WARNING] [MULTI_USRP] Setting IQ imbalance compensation is not possible on this device.
    set_min_output_buffer on block 8 to 10000000
    /root/.gnuradio/prefs/vmcircbuf_default_factory: No such file or directory
    vmcircbuf_createfilemapping: createfilemapping is not available
    Press Enter to quit: TX UDP : {"FTX": "910e6", "FRX": "900e6"}
    TX UDP : {"print": ""}
    FTX = 910000000.0

    FRX = 900000000.0

    GTX = 30.0

    GRX = 20.0

    TX UDP : {"GRX": "15"}
    TX UDP : {"print": ""}
    FTX = 910000000.0

    FRX = 900000000.0

    GTX = 30.0

    GRX = 15.0

The receiving gain should indeed be set to 15.

Now enter the following commands in order to send a message test from the node to the base station

    Enter the parameter OR "send" to send all stored commands OR "print" to display the current parameters: MSG 
    Enter the new value of the Data to transmit :test
    Command added to list : {'MSG': 'test'}

    Enter the parameter OR "send" to send all stored commands OR "print" to display the current parameters: send

Check out the results on terminal NODE.phy.

    TX UDP : {"MSG": "test"}
    U--------Header--------
    Payload length: 8
    CRC presence: 1
    Coding rate: 4
    Header checksum valid!

    msg: ACK-test

    CRC valid!

    RX UDP :{'msg_avg': '0', 'snr_avg': '0', 'CR': '4', 'MSG_Energy': '-2.77802', 'err_corrected': '0', 'err_detected': '0', 'Noise_Energy': '-49.6229', 'pay_len': '8', 'SNR': '46.8449', 'msg': 'ACK-test', 'crc_valid': 'True', 'SF': '7'}

Let's try to understand it:

  • The first line confirms that the message “test” has indeed been sent.
  • The node then waits for an acknoledgment from the base station and enters receiving mode.
  • The next ones concern the received acknoldgement. Before understanding them, check out the other terminals, starting with terminal BS.phy.
    --------Header--------
    Payload length: 4
    CRC presence: 1
    Coding rate: 4
    Header checksum valid!

    msg: test

    CRC valid!

    RX UDP :{'msg_avg': '0', 'snr_avg': '0', 'CR': '4', 'MSG_Energy': '-3.02165', 'err_corrected': '0', 'err_detected': '0', 'Noise_Energy': '-94.9842', 'pay_len': '4', 'SNR': '91.9625', 'msg': 'test', 'crc_valid': 'True', 'SF': '7'}
    TX UDP : {"MSG": "ACK-test"}

This confirms that the physical layer received a message “test”, and the RX UDP line shows the information sent via UDP protocol to the upper layer on terminal BS.up.

    New message : 

        msg_avg : 0
        snr_avg : 0
        CR : 4
        MSG_Energy : -3.02165
        err_corrected : 0
        err_detected : 0
        Noise_Energy : -94.9842
        pay_len : 4
        SNR : 91.9625
        msg : test
        crc_valid : True
        SF : 7


    Sending Acknowledgement

On this last terminal, you should first see these information, and then the reaction of the base station upper layer: sending an acknoledgment.

This acknoledgment is then sent by the physical layer as indicated by the TX UDP line on terminal BS.phy, and finally received by the physical layer of the node:

    U--------Header--------
    Payload length: 8
    CRC presence: 1
    Coding rate: 4
    Header checksum valid!

    msg: ACK-test

    CRC valid!

    RX UDP :{'msg_avg': '0', 'snr_avg': '0', 'CR': '4', 'MSG_Energy': '-2.77802', 'err_corrected': '0', 'err_detected': '0', 'Noise_Energy': '-49.6229', 'pay_len': '8', 'SNR': '46.8449', 'msg': 'ACK-test', 'crc_valid': 'True', 'SF': '7'}

Customized experiment

This experiment is quite basic, even though it demonstrates how to connect an upper layer to the dynamic LoRa physical layer and to change the physical layer parameter. The node only sends a message and waits for an acknoledgment of the base station. The purpose of the physical layer is to be used by more complex upper layers.

In this part, we will see how to modify the upper layer and redeploy it on CorteXlab. If you want to develop your own upper layer, you can refer to this tutorial to deploy it.

Create your docker image

Using docker, we will enable on your laptop an environment suitable for Cortexlab with gnuradio and needed software.
Your goal is to modify the upper layer, and in this tutorial more specifically the base station upper layer. It will only modify the environment of the image and not your laptop. Then you will be able to save and deploy the new image on cortexlab, without having to re-install everything and dealing with software compatibility and versioning.

Therefore, you can retrieve and run the image deployed on CorteXlab in the first part of the tutorial.

you@yourpc:~$ docker pull amauryparis/cxlb_lora:master
you@yourpc:~$ docker run -dti --net=host --expose 2222 --privileged [--name CONTAINER_NAME] amauryparis/cxlb_lora:master

Access the docker container using ssh:

you@yourpc:~$ ssh -Xp 2222 root@localhost

Add this option to the run command if you want to use a USRP plugged in with a USB port on you local machine: -v /dev/bus/usb:/dev/bus/usb . Also, a new host key is created when running the container. The following command could be necessary to add the new key to the known hosts list before using ssh.

you@yourpc:~$ ssh-keygen -f "/home/[YOUR NAME]/.ssh/known_hosts" -R "[localhost]:2222"

Customize the UDP Base Station

Here, we will just add a simple functionnality to the base station. If the SNR is higher than a threshold, then the transmission gain of the BS will be reduced, and if it is lower than a second threshold, then the gain will be augmented.

This simulates a simple logic behavior: if the receiving SNR is low, then the acknoledgment should be emitted with a high gain in order to be correctly received by the node. On the contrary, if the receiving SNR is very high, then there is no need to emit with a high gain.

In order to do so, we will modify the udp_BS.py file: replace lines 48 to 53 included with the following code:

snippet.python
    # Tx gain settings
    snr = received_msg["SNR"]
    # print("SNR : " + snr)
    if float(snr) > 80:
        print("High SNR - Decrease Tx Gain to 10")
        cmd_dict = {
                        "GTX": "10", 
                    }
    elif float(snr) < 60:
        print("Low SNR - increase Tx Gain to 30")
        cmd_dict = {
                        "GTX": "30", 
                    }
 
 
    # Send ack
    print("Sending Acknowledgement")
    socket_tx = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    socket_tx.connect((IP_ADDRESS, PORT_NO_TX))
 
    cmd_dict.update({ "MSG": str("ACK-" + str(received_msg["msg"])) }) 

In order to edit udp_BS.py you can install vim in your docker container:

  root@yourpcwdocker:~# apt install vim

We can test it on our local machine before sending it to cortexlab (it won't work if you don't have an USRP available).

Once you are satisfied with your modifications and the scripts are working properly, you can exit the container with Ctrl + d. Now, let's save our updated container as a new docker image. We can then push it on dockerhub (dockerhub is a repository of docker images, you need to create an account on it).

you@yourpc:~$ docker ps -a 
you@yourpc:~$ docker commit [CONTAINER ID] [NEW IMAGE NAME] 
you@yourpc:~$ docker tag [NEW IMAGE NAME] [DOCKER USERNAME]/[NEW IMAGE NAME] 
you@yourpc:~$ docker login 
you@yourpc:~$ docker push [DOCKER USERNAME]/[NEW IMAGE NAME] 

Deploy the experiment on CorteXlab

In order to send this environment on CorteXlab, follow the same procedure as in subsections Create the scenario to Setup. Just change the docker image name in scenario.yaml from amauryparis/cxlb_lora:master to [DOCKER USERNAME]/[IMAGE NAME].

Results

You should now follow the same steps as in the first part to connect to the nodes 14 and 16 with four terminals, refered to as NODE.phy, NODE.up, BS.phy and BS.up, and execute lora_dyn_node.py, udp_Node.py and udp_BS.py.

Default GTX

First send a message with the default GTX on terminal NODE.up.

Enter the parameter OR "send" to send all stored commands OR "print" to display the current parameters: MSG 
Enter the new value of the Data to transmit :test
Command added to list : {'MSG': 'test'}

Enter the parameter OR "send" to send all stored commands OR "print" to display the current parameters: send

You should now see the following answer on terminal NODE.phy.

    TX UDP : {"MSG": "test"}
    U--------Header--------
    Payload length: 8
    CRC presence: 1
    Coding rate: 4
    Header checksum valid!

    msg: ACK-test

    CRC valid!

    RX UDP :{'msg_avg': '0', 'snr_avg': '0', 'CR': '4', 'MSG_Energy': '-14.0058', 'err_corrected': '0', 'err_detected': '0', 'Noise_Energy': '-75.117', 'pay_len': '8', 'SNR': '61.1112', 'msg': 'ACK-test', 'crc_valid': 'True', 'SF': '7'}
    TX UDP : {"MSG": "test"}

And the following ones on terminal BS.phy and BS.up.

  • BS.phy
    --------Header--------
    Payload length: 4
    CRC presence: 1
    Coding rate: 4
    Header checksum valid!

    msg: test

    CRC valid!

    RX UDP :{'msg_avg': '0', 'snr_avg': '0', 'CR': '4', 'MSG_Energy': '-4.2716', 'err_corrected': '0', 'err_detected': '0', 'Noise_Energy': '-95.7583', 'pay_len': '4', 'SNR': '91.4867', 'msg': 'test', 'crc_valid': 'True', 'SF': '7'}
    TX UDP : {"GTX": "10", "MSG": "ACK-test"}
  • BS.up
    New message : 

        msg_avg : 0
        snr_avg : 0
        CR : 4
        MSG_Energy : -4.2716
        err_corrected : 0
        err_detected : 0
        Noise_Energy : -95.7583
        pay_len : 4
        SNR : 91.4867
        msg : test
        crc_valid : True
        SF : 7


    High SNR - Decrease Tx Gain to 10
    Sending Acknowledgement

In this case, the SNR is higher than 90 so the base station GTX is set to 10 instead of 30.

The acknoledgment is still received correctly by the node as shown in terminal NODE.phy.

Simulate distance

In order to simulate a less ideal situation and reduce the SNR, you can set the node GTX to 5 in terminal NODE.up and send a new message.

    Enter the parameter OR "send" to send all stored commands OR "print" to display the current parameters: GTX
    Enter the new value of the Gain for TX chain :5
    Command added to list : {'GTX': '5'}

    Enter the parameter OR "send" to send all stored commands OR "print" to display the current parameters: MSG 
    Enter the new value of the Data to transmit :test5
    Command added to list : {'GTX': '5', 'MSG': 'test5'}

    Enter the parameter OR "send" to send all stored commands OR "print" to display the current parameters: send

Look at the answers in the other terminals:

  • NODE.phy
    TX UDP : {"GTX": "5", "MSG": "test5"}
    U--------Header--------
    Payload length: 9
    CRC presence: 1
    Coding rate: 4
    Header checksum valid!

    msg: ACK-test5

    CRC valid!

    RX UDP :{'msg_avg': '0', 'snr_avg': '0', 'CR': '4', 'MSG_Energy': '-3.34723', 'err_corrected': '0', 'err_detected': '0', 'Noise_Energy': '-85.2143', 'pay_len': '9', 'SNR': '81.8671', 'msg': 'ACK-test5', 'crc_valid': 'True', 'SF': '7'}
  • BS.phy
    --------Header--------
    Payload length: 5
    CRC presence: 1
    Coding rate: 4
    Header checksum valid!

    msg: test5

    CRC valid!

    RX UDP :{'msg_avg': '0', 'snr_avg': '0', 'CR': '4', 'MSG_Energy': '-25.0943', 'err_corrected': '0', 'err_detected': '0', 'Noise_Energy': '-95.796', 'pay_len': '5', 'SNR': '70.7018', 'msg': 'test5', 'crc_valid': 'True', 'SF': '7'}
    TX UDP : {"GTX": "30", "MSG": "ACK-test5"}
  • BS.up
    New message : 

        msg_avg : 0
        snr_avg : 0
        CR : 4
        MSG_Energy : -25.0943
        err_corrected : 0
        err_detected : 0
        Noise_Energy : -95.796
        pay_len : 5
        SNR : 70.7018
        msg : test5
        crc_valid : True
        SF : 7


Low SNR - increase Tx Gain to 30
Sending Acknowledgement

The message is still received by the base station, but since the SNR is lower, the base station rises its GTX to 30, so that the acknoledgment is received by the node.

What next?

Congratulations! You have finished your second tutorial on CorteXlab. After this tutorial, you should now be able to adapt any existing upper layer to the udp interface of the LoRa physical layer, or even create new ones!

lora_dynamic_phy_layer_example.txt · Last modified: 2021/07/08 11:35 by pesteve