ris_operation_tutorial
Differences
This shows you the differences between two versions of the page.
| Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
| ris_operation_tutorial [2026/09/02 15:57] – [The scenario] cmorin | ris_operation_tutorial [2026/09/04 14:42] (current) – cmorin | ||
|---|---|---|---|
| Line 1: | Line 1: | ||
| # RIS Operation Tutorial | # RIS Operation Tutorial | ||
| - | |||
| - | To Fill In | ||
| **This Tutorial assumes that you have followed the basic CorteXlab operation tutorials (at least [[running_your_first_experiment|GNU Radio benchmark example]], and [[bokehgui_for_cortexlab|Eyes and ears inside CorteXlab]], | **This Tutorial assumes that you have followed the basic CorteXlab operation tutorials (at least [[running_your_first_experiment|GNU Radio benchmark example]], and [[bokehgui_for_cortexlab|Eyes and ears inside CorteXlab]], | ||
| Line 53: | Line 51: | ||
| * Configuration algorithms: | * Configuration algorithms: | ||
| * `/ | * `/ | ||
| - | | + | * `/ |
| * Managing configuration files with `/ | * Managing configuration files with `/ | ||
| Line 69: | Line 67: | ||
| * Setup the RIS node with the provided docker image to allow for RIs control | * Setup the RIS node with the provided docker image to allow for RIs control | ||
| * Setup node 18, on the other side of the wall, to receive the signal and display received power and channel frequency response information, | * Setup node 18, on the other side of the wall, to receive the signal and display received power and channel frequency response information, | ||
| - | |||
| Line 76: | Line 73: | ||
| ## Running it | ## Running it | ||
| + | |||
| + | ### Get the files | ||
| + | |||
| + | The files we are going to use live inside the same repository as the REST API. | ||
| + | Let's go to the tutorial folder to get those: | ||
| + | |||
| + | |||
| + | < | ||
| + | you@srvairlock: | ||
| + | you@srvairlock: | ||
| + | you@srvairlock: | ||
| + | you@srvairlock: | ||
| + | </ | ||
| + | |||
| + | |||
| + | We will be using the contents of the examples/ | ||
| + | |||
| + | |||
| + | < | ||
| + | you@srvairlock: | ||
| + | you@srvairlock: | ||
| + | power_reader_epy_block_0.py | ||
| + | </ | ||
| + | |||
| + | Let's go over each one of the files in this folder: | ||
| + | |||
| + | * '' | ||
| + | * '' | ||
| + | * '' | ||
| + | * '' | ||
| + | * '' | ||
| + | * '' | ||
| + | |||
| + | Edit the scenario file to point the commands to the user's folder | ||
| + | |||
| + | Let's open the existing scenario file using nano (or some installed text editor that you may prefer): | ||
| + | |||
| + | < | ||
| + | you@srvairlock: | ||
| + | </ | ||
| + | |||
| + | < | ||
| + | # Scenario textual description | ||
| + | description: | ||
| + | |||
| + | # Experiment maximum duration | ||
| + | duration: 1800 | ||
| + | |||
| + | |||
| + | nodes: | ||
| + | node18: | ||
| + | container: | ||
| + | - image: ghcr.io/ | ||
| + | exec: | ||
| + | - bash -lc "pip install flask && apt install curl" | ||
| + | command: bash -lc " | ||
| + | node38: | ||
| + | container: | ||
| + | - image: ghcr.io/ | ||
| + | command: bash -lc " | ||
| + | |||
| + | node31: | ||
| + | container: | ||
| + | - image: registry.gitlab.inria.fr/ | ||
| + | passive: true | ||
| + | </ | ||
| + | |||
| + | **Make sure you edit the command paths for TX and RX to point to your own folder (replace with your username)** | ||
| + | |||
| + | As you can see here, the scenario file is very similar to what was used in previous tutorials. | ||
| + | For node 31, driving the RIS itself, the only thing required is to specify the ris-api docker image. | ||
| + | No need for a command, server startup is automatic. | ||
| + | |||
| + | The only exotic element here would be for node 18: | ||
| + | < | ||
| + | exec: | ||
| + | - bash -lc "pip install flask && apt install curl" | ||
| + | </ | ||
| + | This option allows for execution of extra commands in parallel of the main one. | ||
| + | We use it to install flask and curl, two packages we want to use to communicate with the RIS, on the fly, without having to generate a dedicated docker image. | ||
| + | |||
| + | |||
| + | ### (Optional) Explore the GRC files | ||
| + | Let's see what's in the scripts we are going to run. | ||
| + | You can look inside the .py file or open the .grc files in your own installation of GNU Radio Companion but here are some screenshots: | ||
| + | |||
| + | {{ :: | ||
| + | |||
| + | The TX side is really simple, we generate a sequence (here a Zadoff-Chu of 2048 samples) that we send on repeat to the radio block. | ||
| + | |||
| + | {{ :: | ||
| + | |||
| + | On the RX side, we display first the raw received frequencies, | ||
| + | On top of that, we have quite a few elements to interact with the RIS. | ||
| + | |||
| + | The main one sits inside the HTTP Helper block. It's a custom Python block to make the HTTP requests to the RIS server. Its code is inside '' | ||
| + | |||
| + | < | ||
| + | class http_helper(gr.sync_block): | ||
| + | """ | ||
| + | |||
| + | def __init__(self, | ||
| + | """ | ||
| + | gr.sync_block.__init__( | ||
| + | self, | ||
| + | name=' | ||
| + | in_sig=[], | ||
| + | out_sig=[] | ||
| + | ) | ||
| + | # if an attribute with the same name as a parameter is found, | ||
| + | # a callback is registered (properties work, too). | ||
| + | self.ris_node = ris_node | ||
| + | |||
| + | self.my_log = gr.logger(self.alias()) | ||
| + | |||
| + | # def work(self, input_items, | ||
| + | # """ | ||
| + | # | ||
| + | # | ||
| + | | ||
| + | def turn_on(self): | ||
| + | r = requests.get(f" | ||
| + | print(f" | ||
| + | return r.text | ||
| + | | ||
| + | |||
| + | def turn_off(self): | ||
| + | r = requests.get(f" | ||
| + | print(f" | ||
| + | return r.text | ||
| + | | ||
| + | | ||
| + | |||
| + | def reset(self, val=1): | ||
| + | r = requests.post(f" | ||
| + | if r.status_code != requests.codes.ok: | ||
| + | print(f" | ||
| + | return float(r.text) | ||
| + | return | ||
| + | |||
| + | def ref_optim(self, | ||
| + | self.my_log.warn(f" | ||
| + | |||
| + | req_url = f'curl " | ||
| + | self.my_log.warn(req_url) | ||
| + | subprocess.Popen(req_url, | ||
| + | |||
| + | |||
| + | def load_config(self): | ||
| + | r = requests.get(f" | ||
| + | print(r.text) | ||
| + | |||
| + | def get_config(self): | ||
| + | response = requests.get(f" | ||
| + | self.my_log.warn(f" | ||
| + | vector = response.json() | ||
| + | ris_c = np.array(vector[' | ||
| + | self.my_log.warn(ris_c) | ||
| + | |||
| + | def Beamform(self, | ||
| + | params = { | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | } | ||
| + | | ||
| + | response = requests.get(f" | ||
| + | self.my_log.warn(response) | ||
| + | self.my_log.warn(f" | ||
| + | </ | ||
| + | All its functions send HTTP requests through the '' | ||
| + | |||
| + | As you can see with their parameters, all the Bokeh GUI Button blocks are there to call functions of this HTTP Helper block. | ||
| + | |||
| + | |||
| + | All this is sufficient for all RIS commands, except for, again, the ''/ | ||
| + | This one requires feedback to evaluate the configuration it tries, in the form of another HTTP server, replying with a float value, whenever queried on the ''/ | ||
| + | |||
| + | In our case, it's implemented inside of the same RX flowgraph, as a python snippet this time, that contains the following: | ||
| + | < | ||
| + | from flask import Flask | ||
| + | import threading | ||
| + | |||
| + | def server(): | ||
| + | app = Flask(__name__) | ||
| + | |||
| + | @app.route('/ | ||
| + | def power_feedback(): | ||
| + | |||
| + | read_power = self.blocks_probe_signal_x_0.level() | ||
| + | return f" | ||
| + | |||
| + | app.run(port=5002, | ||
| + | |||
| + | |||
| + | self.my_log = gr.logger(self.alias()) | ||
| + | server_thread = threading.Thread(target=server) | ||
| + | server_thread.daemon = True | ||
| + | server_thread.start() | ||
| + | </ | ||
| + | |||
| + | We setup a Flask server inside of a separate thread, that implements only the ''/ | ||
| + | And that endpoint queries the Probe Signal block at the end of the flowgraph to respond with the latest computed average received power. | ||
| + | |||
| + | Finally, the Fast Multiply Const block just before the Probe Signal uses the value of the Bokeh GUI Checkbox to inverse the signal metric when the box is checked. | ||
| + | |||
| + | ### Run the task | ||
| + | |||
| + | We will use the usual commands to run the task: | ||
| + | < | ||
| + | you@srvairlock: | ||
| + | Creating the task file... | ||
| + | Task file scenario.task created successfully. | ||
| + | you@srvairlock: | ||
| + | 25062 | ||
| + | </ | ||
| + | |||
| + | ### Connect browser to the display | ||
| + | |||
| + | In the previous tutorial about remote monitoring, [[bokehgui_for_cortexlab|Eyes and ears inside CorteXlab]], | ||
| + | |||
| + | Here, we'll show an other option, that requires a bit more setup, but allows for more flexibility once it's done: SOCKS proxy. | ||
| + | |||
| + | If you don't want to use that option, or if it doesn' | ||
| + | You would simply need to start an ssh deamon on node 18 to be able to connect to it. | ||
| + | For that, you need to add an extra exec line on the node, like so: | ||
| + | |||
| + | < | ||
| + | ... | ||
| + | nodes: | ||
| + | node18: | ||
| + | container: | ||
| + | - image: ghcr.io/ | ||
| + | exec: | ||
| + | - / | ||
| + | - bash -lc "pip install flask && apt install curl" | ||
| + | command: bash -lc " | ||
| + | ... | ||
| + | </ | ||
| + | |||
| + | Back to the SOCKS proxy method. | ||
| + | We first need to open that SSH proxy with an extra option to the SSH command so, in a new terminal: | ||
| + | |||
| + | < | ||
| + | you@yourpc: | ||
| + | </ | ||
| + | |||
| + | Proxy is now open on port 4321. | ||
| + | What's left is to configure your browser to use it. | ||
| + | Many websites explain how to do it for many browsers, better than we could do here, for instance, [[https:// | ||
| + | The port to setup is **4321**, same as we specified with the ssh -D option. | ||
| + | And the proxy is running locally, so the server address is **127.0.0.1** | ||
| + | |||
| + | Extensions are also available to make the proxy configuration and switching easier, such as [[https:// | ||
| + | |||
| + | Once the configuration is done, provided the task is still running, you can connect to it by pointing your browser to the node's URL: | ||
| + | '' | ||
| + | |||
| + | It should show an interface similar to this with a bunch of controls on the left, a frequency response plot, and a Received power plot: | ||
| + | |||
| + | {{ :: | ||
| + | |||
| + | First turn on the RIS by clicking on the corresponding button. It's normal that the plots don't change at this point. | ||
| + | Then click on the '' | ||
| + | You should see the plots moving up and down rapidly over a few seconds, eventually settling a few dB higher than where it started. You did your first RIS configuration, | ||
| + | |||
| + | To see the difference, with the default state, either turn off the RIS (don't forget to turn it back on after that), or click on the '' | ||
| + | |||
| + | {{ :: | ||
| + | |||
| + | With the '' | ||
| + | |||
| + | You can play with the '' | ||
| + | |||
| + | On the right of each of the two plots, the toolbar contains a button to reset the max value line. It's the third one from the bottom, with a tooltip reading appropriately '' | ||
| + | |||
| + | |||
| + | Feel free to play with all the parameters such as the receive gain, TX/RX frequency or, to see more dramatic results, reverse the optimisation, | ||
| + | |||
| + | |||
| + | The Elevation, Azimuth and Distance parameters go with the Beamform button. It triggers the narrow beam beam forming algorithm, setting a configuration based on those geometric parameters. | ||
| + | |||
| + | For Both algorithms, you can get the optimised configuration with the '' | ||
| ## Conclusion | ## Conclusion | ||
| + | This tutorial and its code demonstrates the remote RIS operation, with all its available commands. | ||
| + | Here, everything is done inside a GNU Radio flowgraph but, since since communication goes through standard HTTP, you could use raw python, or whatever language you prefer. | ||
| + | Call could even be made on the terminal, from airlock or your own (with the SOCKS proxy and a CLI utility like tsocks), or from the browser debug menu. | ||
ris_operation_tutorial.1788364661.txt.gz · Last modified: by cmorin
