From a9d05ca975fe1e6691f18b53890e757d91c591ac Mon Sep 17 00:00:00 2001 From: Christos Falas Date: Sun, 29 Dec 2024 12:24:21 +0000 Subject: [PATCH] Support multiple receiving hosts --- src/aoa.py | 11 ++--- src/config.py | 15 +++++-- src/main.py | 109 ++++++++++++++++++++++++++++++++++++++------------ 3 files changed, 97 insertions(+), 38 deletions(-) diff --git a/src/aoa.py b/src/aoa.py index b7a8bd8..bae2990 100644 --- a/src/aoa.py +++ b/src/aoa.py @@ -34,15 +34,10 @@ class AoA: for j in range(M // 2): H_n[i, j] = data[j : j + M // 2 + 1, i, 0] - H_sm = np.vstack([H_n[0], H_n[1]]) + H_sm_rows = [np.hstack(H_n[i : i + N // 2 + 1]) for i in range(N // 2)] + H_sm = np.vstack(H_sm_rows) - # This would normally have to be arranged as follows: - # H_0 H_1 ... H_{N//2 - 1} - # H_1 H_2 ... H_{N//2} - # ... - # H_{N//2} ... H{N-1} - # But this doesn't work when we only have 2 receiving antennas - assert N == 2, "The current implementation only supports 2 RX antennas" + logger.info(f"Smoothed: {H_sm.shape}") return H_sm diff --git a/src/config.py b/src/config.py index e461b69..699829b 100644 --- a/src/config.py +++ b/src/config.py @@ -6,20 +6,27 @@ PREPROCESSING_LONG_TERM_ALPHA = 0.01 PREPROCESSING_BANDPASS_LOW_CUTOFF = 2 PREPROCESSING_BANDPASS_HIGH_CUTOFF = 40 -AOA_SLIDING_WINDOW_SIZE = 20 +AOA_SLIDING_WINDOW_SIZE = 40 -RECEIVE_IP_ADDRESS = os.getenv("IP_ADDRESS", "10.0.12.64") +RECEIVE_IP_ADDRESS_LIST = ["10.0.12.64", "10.0.12.62"] INJECT_IP_ADDRESS = os.getenv("INJECT_IP_ADDRESS", "10.0.12.63") FEITCSI_PORT = 8008 +ANTENNA_ORDER = [ + ("10.0.12.62", 0), + ("10.0.12.64", 1), + ("10.0.12.64", 0), + ("10.0.12.62", 1), +] + SAMPLE_RATE = 100 # Hz -EIGVAL_THRESHOLD = 1000 +EIGVAL_THRESHOLD = 10 # DELTA_F = 78_125 # Spacing between subcarriers in Hz DELTA_F = 312_500 # Spacing between subcarriers in Hz -CENTRAL_FREQUENCY_MHZ = 5220 +CENTRAL_FREQUENCY_MHZ = 6195 ANTENNA_SPACING = 0.0285 # 2.85 cm CHANNEL_WIDTH = 20 FRAME_FORMAT = "HT" diff --git a/src/main.py b/src/main.py index 6288dd4..7a42eb7 100644 --- a/src/main.py +++ b/src/main.py @@ -3,6 +3,8 @@ import socket from typing import Callable import threading import struct +from datetime import datetime +import numpy as np from .csi import CSI from .aoa import AoA @@ -12,28 +14,16 @@ from . import visualise logging.basicConfig( level=logging.INFO, - format="%(asctime)s %(levelname)-8s %(message)s", + format="%(asctime)s %(name)-8s %(levelname)-8s %(message)s", ) +logger = logging.getLogger(__name__) -class FeitServer: - def __init__( - self, - ): - self.port = config.FEITCSI_PORT - self.server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - self.server.connect((config.RECEIVE_IP_ADDRESS, self.port)) - self.start_string = ( - f"feitcsi --frequency {config.CENTRAL_FREQUENCY_MHZ} " - f"--channel-width {config.CHANNEL_WIDTH} " - f"--format {config.FRAME_FORMAT} " - f"--mode measure" - ) - self.server.send(b"stop\n") - self.server.send(self.start_string.encode()) +class FeitTransmitter: + def __init__(self): self.inject_server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - self.inject_server.connect((config.INJECT_IP_ADDRESS, self.port)) + self.inject_server.connect((config.INJECT_IP_ADDRESS, config.FEITCSI_PORT)) inject_start_string = ( f"feitcsi --frequency {config.CENTRAL_FREQUENCY_MHZ} " f"--channel-width {config.CHANNEL_WIDTH} " @@ -44,7 +34,23 @@ class FeitServer: self.inject_server.send(b"stop\n") self.inject_server.send(inject_start_string.encode()) - def listen(self, callback: Callable[[CSI], None]): + +class FeitServer: + def __init__(self, host: str): + self.host = host + self.server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + self.server.connect((self.host, config.FEITCSI_PORT)) + self.start_string = ( + f"feitcsi --frequency {config.CENTRAL_FREQUENCY_MHZ} " + f"--channel-width {config.CHANNEL_WIDTH} " + f"--format {config.FRAME_FORMAT} " + f"--mode measure" + ) + self.server.send(b"stop\n") + self.server.send(self.start_string.encode()) + + def listen(self, callback: Callable[[str, CSI], None]): + prev_time = datetime.now() while True: # This is the max size of a UDP packet. The size of the actual CSI # packet will depend on the frame format and channel width, which @@ -52,20 +58,66 @@ class FeitServer: data = self.server.recv(65535) try: csidata = CSI(data) - callback(csidata) + logger.debug(f"Received CSI data after {datetime.now() - prev_time}") + prev_time = datetime.now() + callback(self.host, csidata) except struct.error: - logging.error("Failed to parse CSI data") + logger.error("Failed to parse CSI data") -def process_data(data: CSI): - logging.debug("Got CSI frame") - processed = preprocess.preprocess(data) - visualise.add_data(processed) +pending_data: dict[str, tuple[datetime, CSI]] = {} +pending_data_lock = threading.Lock() + + +def process_data(host: str, data: CSI): + global pending_data + current_time = datetime.now() + + logger.debug(f"Got CSI data from {host}") + + with pending_data_lock: + if host in pending_data and ( + current_time - pending_data[host][0] + ).total_seconds() < 1 / (2 * config.SAMPLE_RATE): + logger.warning(f"Received CSI data from {host} too quickly") + return + elif host in pending_data: + logger.warning(f"Skipping existing CSI data from {host}") + pending_data[host] = (datetime.now(), data) + + for ip in config.RECEIVE_IP_ADDRESS_LIST: + if ip not in pending_data: + return + + logger.debug( + "Antenna RSSI values:" + + str( + [ + (ip, csi.header.rssi1, csi.header.rssi2) + for ip, (_, csi) in sorted(pending_data.items()) + ] + ), + ) + + antenna_data = [ + np.expand_dims(pending_data[ip][1].matrix[:, antenna], axis=2) + for ip, antenna in config.ANTENNA_ORDER + ] + + # We have data from all servers + all_data = np.concat(antenna_data, axis=1) + logger.info(f"Got final CSI data with shape {all_data.shape}") + pending_data = {} + + # logging.info(f"Got CSI frame with shape {data.matrix.shape}") + processed = preprocess.preprocess(all_data) + visualise.add_data(data.matrix, processed) aoa.update(processed) if __name__ == "__main__": - server = FeitServer() + servers = [FeitServer(ip) for ip in config.RECEIVE_IP_ADDRESS_LIST] + transmitter = FeitTransmitter() preprocess = Preprocessor() aoa = AoA() @@ -74,4 +126,9 @@ if __name__ == "__main__": webapp = threading.Thread(target=visualise.start) webapp.start() - server.listen(process_data) + server_threads = [ + threading.Thread(target=server.listen, args=(process_data,)) + for server in servers + ] + for thread in server_threads: + thread.start()