Support multiple receiving hosts

This commit is contained in:
Christos Falas 2024-12-29 12:24:21 +00:00
parent 25e259f038
commit a9d05ca975
No known key found for this signature in database
3 changed files with 97 additions and 38 deletions

View File

@ -34,15 +34,10 @@ class AoA:
for j in range(M // 2): for j in range(M // 2):
H_n[i, j] = data[j : j + M // 2 + 1, i, 0] 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: logger.info(f"Smoothed: {H_sm.shape}")
# 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"
return H_sm return H_sm

View File

@ -6,20 +6,27 @@ PREPROCESSING_LONG_TERM_ALPHA = 0.01
PREPROCESSING_BANDPASS_LOW_CUTOFF = 2 PREPROCESSING_BANDPASS_LOW_CUTOFF = 2
PREPROCESSING_BANDPASS_HIGH_CUTOFF = 40 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") INJECT_IP_ADDRESS = os.getenv("INJECT_IP_ADDRESS", "10.0.12.63")
FEITCSI_PORT = 8008 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 SAMPLE_RATE = 100 # Hz
EIGVAL_THRESHOLD = 1000 EIGVAL_THRESHOLD = 10
# DELTA_F = 78_125 # Spacing between subcarriers in Hz # DELTA_F = 78_125 # Spacing between subcarriers in Hz
DELTA_F = 312_500 # 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 ANTENNA_SPACING = 0.0285 # 2.85 cm
CHANNEL_WIDTH = 20 CHANNEL_WIDTH = 20
FRAME_FORMAT = "HT" FRAME_FORMAT = "HT"

View File

@ -3,6 +3,8 @@ import socket
from typing import Callable from typing import Callable
import threading import threading
import struct import struct
from datetime import datetime
import numpy as np
from .csi import CSI from .csi import CSI
from .aoa import AoA from .aoa import AoA
@ -12,28 +14,16 @@ from . import visualise
logging.basicConfig( logging.basicConfig(
level=logging.INFO, 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 = 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 = ( inject_start_string = (
f"feitcsi --frequency {config.CENTRAL_FREQUENCY_MHZ} " f"feitcsi --frequency {config.CENTRAL_FREQUENCY_MHZ} "
f"--channel-width {config.CHANNEL_WIDTH} " f"--channel-width {config.CHANNEL_WIDTH} "
@ -44,7 +34,23 @@ class FeitServer:
self.inject_server.send(b"stop\n") self.inject_server.send(b"stop\n")
self.inject_server.send(inject_start_string.encode()) 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: while True:
# This is the max size of a UDP packet. The size of the actual CSI # 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 # packet will depend on the frame format and channel width, which
@ -52,20 +58,66 @@ class FeitServer:
data = self.server.recv(65535) data = self.server.recv(65535)
try: try:
csidata = CSI(data) 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: except struct.error:
logging.error("Failed to parse CSI data") logger.error("Failed to parse CSI data")
def process_data(data: CSI): pending_data: dict[str, tuple[datetime, CSI]] = {}
logging.debug("Got CSI frame") pending_data_lock = threading.Lock()
processed = preprocess.preprocess(data)
visualise.add_data(processed)
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) aoa.update(processed)
if __name__ == "__main__": if __name__ == "__main__":
server = FeitServer() servers = [FeitServer(ip) for ip in config.RECEIVE_IP_ADDRESS_LIST]
transmitter = FeitTransmitter()
preprocess = Preprocessor() preprocess = Preprocessor()
aoa = AoA() aoa = AoA()
@ -74,4 +126,9 @@ if __name__ == "__main__":
webapp = threading.Thread(target=visualise.start) webapp = threading.Thread(target=visualise.start)
webapp.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()