+
+
+### User-interface
+How to Launch the user-interface:
+
+- After completing the initial build, open a new terminal and navigate into `/sw`
+- Run the command to laod your anaconda environment:
+```
+ conda activate wulpus_env
+```
+- To start the system, execute
+```
+ python -m wulpus.main
+```
+You should be able to open a browser of you choice and visit [http://127.0.0.1:8000/](http://127.0.0.1:8000/)
+
+
+#### Using the log-file
+The log which gets recorded by the userinterface is a zip-file called `wulpus-{date}-id-{connection}.zip`.
+The date is the start of the recording, with the connection describes which wulpus was used (MAC address or COM port).
+It contains the config from the job, as well as the data in a [parquet](https://parquet.apache.org/) file.
+
+The parquet-file contains the following rows:
+- `tx`: list of channels used for sending
+- `rx`: list of channels used for receiving
+- `aq_number`: ID increased by the wulpus (can be used to detect lost frames)
+- `log_version`: the current version of the log-format. Constant over the whole file.
+- `tx_rx_id`: the index of the tx-rx-config used
+- `0`, `1`, `2`, ...`{num samples - 1}`: the actual data, readout from ADC
+- `__index_level_0__`: unix timestamp in us
+
+You can use the jupyter notebook [visualize_log.ipynb](visualize_log.ipynb) to load a single or a set of multiple measurements and evaluate them.
+Ther's also [MATLAB_load_wulpus_log.m](MATLAB_load_wulpus_log.m) to help import the logs into MATLAB.
+
+
+### Jupyter notebook (legacy)
+How to Launch an example Jupyter notebook:
+- In a terminal launch
+```
+ conda activate wulpus_env
+```
+- Run the command below:
+
+## Development
+If you want to work on the frontend, it's easier to just run the backen (`wulpus`) and the frontend (`wulpus-frontend`) seperate.
+This way you don't have to build after each step.
+
+You can start the backend as usual (see above `python -m wulpus.main`), but instead of visiting [http://127.0.0.1:8000/](http://127.0.0.1:8000/) in your browser, you start the dev-environment of the frontend:
+- Open an additonal terminal at `\sw\wulpus-frontend`
+- run
+```
+ npm run dev
+```
+- open the displayed link in your browser (probably [http://localhost:5173/](http://localhost:5173/))
+
+This way do you access the frontend though its own dev-server, which does hot-reloading.
+It still acesses the same backend, so everything still works exactly the same.
# License
The source files are released under Apache v2.0 (`Apache-2.0`) license unless noted otherwise, please refer to the `sw/LICENSE` file for details.
\ No newline at end of file
diff --git a/sw/convert_measurements.ipynb b/sw/convert_measurements.ipynb
new file mode 100644
index 0000000..462967b
--- /dev/null
+++ b/sw/convert_measurements.ipynb
@@ -0,0 +1,131 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "60c8ba1d",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "%matplotlib widget\n",
+ "import ipywidgets as widgets\n",
+ "import matplotlib.pyplot as plt\n",
+ "import matplotlib.colors as colors\n",
+ "import numpy as np\n",
+ "import pandas as pd\n",
+ "from datetime import datetime\n",
+ "import os\n",
+ "import time"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "f441f7e2",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "old_file = '.\\\\wulpus\\\\measurements\\\\fliessfront-2025-08-29.npz'\n",
+ "recording = time.strptime(\"2025-08-29 14:00\", \"%Y-%m-%d %H:%M\")\n",
+ "\n",
+ "data = np.load(old_file)\n",
+ "path = os.path.dirname(old_file)\n",
+ "print('Keys:', data.files)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "4fa3f542",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "data_cnt = data['data_arr'].shape[1]\n",
+ "num_samples = data['data_arr'].shape[0]\n",
+ "measurements = data['data_arr'].T\n",
+ "\n",
+ "tx_channel = np.zeros((num_samples,1), dtype=np.uint8)\n",
+ "rx_channel = np.zeros((num_samples,1), dtype=np.uint8)\n",
+ "time = np.zeros(num_samples, dtype=np.float32)\n",
+ "\n",
+ "config = [\n",
+ " [[1, 2, 3, 4, 5, 6, 7], [0]],\n",
+ " [[0, 2, 3, 4, 5, 6, 7], [1]],\n",
+ " [[0, 1, 3, 4, 5, 6, 7], [2]],\n",
+ " [[0, 1, 2, 4, 5, 6, 7], [3]],\n",
+ " [[0, 1, 2, 3, 5, 6, 7], [4]],\n",
+ " [[0, 1, 2, 3, 4, 6, 7], [5]],\n",
+ " [[0, 1, 2, 3, 4, 5, 7], [6]],\n",
+ " [[0, 1, 2, 3, 4, 5, 6], [7]],\n",
+ "]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "102b78be",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "recording_dt = datetime(*recording[:6])\n",
+ "dates = pd.date_range(recording_dt, periods=num_samples, freq=\"s\")\n",
+ "df = pd.DataFrame({\n",
+ " 'measurement': [pd.Series(measurements[i]) for i in range(num_samples)], \n",
+ " \"tx\": [config[i % len(config)][0] for i in range(num_samples)], \n",
+ " \"rx\": [config[i % len(config)][1] for i in range(num_samples)],\n",
+ " \"aq_number\": [i for i in range(num_samples)],\n",
+ " \"log_version\": 1,\n",
+ " \"tx_rx_id\": [i % len(config) for i in range(num_samples)]\n",
+ " }, \n",
+ " index=dates)\n",
+ "df.head(15)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "6416d86f",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Expand measurement series into columns so they're all included in save-format (parquet)\n",
+ "import io\n",
+ "from zipfile import ZipFile\n",
+ "\n",
+ "measurement_expanded = pd.DataFrame(\n",
+ " [m.values for m in df['measurement']], index=df.index)\n",
+ "flattened_df = pd.concat(\n",
+ " [df.drop(columns=['measurement']), measurement_expanded], axis=1)\n",
+ "flattened_df.columns = [str(col) for col in flattened_df.columns]\n",
+ "\n",
+ "with ZipFile(\"converted.zip\", 'w') as zf:\n",
+ " # zf.writestr('config-0.json', config_json.dumps())\n",
+ " # Write dataframe as parquet\n",
+ " buffer = io.BytesIO()\n",
+ " flattened_df.to_parquet(buffer)\n",
+ " zf.writestr('data.parquet', buffer.getvalue())"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": ".venv",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.9.13"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/sw/how_to_install_dependencies.md b/sw/how_to_install_dependencies.md
deleted file mode 100644
index 60fc4cb..0000000
--- a/sw/how_to_install_dependencies.md
+++ /dev/null
@@ -1,23 +0,0 @@
-# How to install Python requirements?
-1. Install Anaconda package manager(.json will be added automatically)
+