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):
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

View File

@ -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"

View File

@ -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()