refactor: switch to processes instead of threads

This commit is contained in:
Christos Falas 2024-12-30 12:29:03 +00:00
parent c2914486a3
commit 0d8bea2678
No known key found for this signature in database
5 changed files with 64 additions and 28 deletions

View File

@ -1,10 +1,11 @@
import logging import logging
import threading import multiprocessing as mp
from multiprocessing.connection import Connection
from typing import NamedTuple
from . import ingest from . import ingest
from . import config from . import config
from . import visualise from . import visualise
from .preprocess import Preprocessor
logging.basicConfig( logging.basicConfig(
@ -12,24 +13,36 @@ logging.basicConfig(
format="%(asctime)s %(name)-40s %(levelname)-8s %(message)s", format="%(asctime)s %(name)-40s %(levelname)-8s %(message)s",
) )
receivers = [ingest.FeitReceiver(ip) for ip in config.RECEIVE_IP_ADDRESS_LIST]
transmitter = ingest.FeitTransmitter()
preprocess = Preprocessor()
processor = ingest.CSIProcessor() class Receiver(NamedTuple):
ip: str
receiver: ingest.FeitReceiver
recv: Connection
send: Connection
# Start webapp in background thread
visualise.aoa = processor.aoa
webapp = threading.Thread(target=visualise.start)
webapp.start()
receiver_threads = [ receivers = [
threading.Thread(target=server.listen, args=(processor.add_data,)) Receiver(ip, ingest.FeitReceiver(ip), *mp.Pipe(duplex=False))
for server in receivers for ip in config.RECEIVE_IP_ADDRESS_LIST
] ]
for thread in receiver_threads: # Start injecting CSI frames
thread.start() transmitter = ingest.FeitTransmitter()
processing_thread = threading.Thread(target=processor.process_forever) webapp_conns = mp.Pipe()
processor = ingest.CSIProcessor({r.ip: r.recv for r in receivers}, webapp_conns[0])
# Start webapp in background process
webapp = mp.Process(target=visualise.start, args=(webapp_conns[1],))
webapp.start()
receiver_processes = [
mp.Process(target=r.receiver.listen, args=(r.send,)) for r in receivers
]
for proc in receiver_processes:
proc.start()
processing_thread = mp.Process(target=processor.process_forever)
processing_thread.start() processing_thread.start()

View File

@ -10,8 +10,8 @@ logger = logging.getLogger(__name__)
class AoA: class AoA:
def __init__(self): def __init__(self):
self.historical_autocorr = np.array([]) self.historical_autocorr = np.array([])
self.N_subcarriers = -1 self.N_subcarriers = config.N_SUBCARRIERS - 2
self.N_rx = -1 self.N_rx = len(config.ANTENNA_ORDER)
pass pass
def smooth(self, data: npt.NDArray[np.complex128]): def smooth(self, data: npt.NDArray[np.complex128]):
@ -21,8 +21,9 @@ class AoA:
N = data.shape[1] # Number of RX antennas N = data.shape[1] # Number of RX antennas
T = data.shape[2] # Number of TX antennas T = data.shape[2] # Number of TX antennas
self.N_subcarriers = M assert N == self.N_rx
self.N_rx = N assert M == self.N_subcarriers
logger.debug(f"Smoothing: Subcarriers: {M}, RX antennas: {N}, TX antennas: {T}") logger.debug(f"Smoothing: Subcarriers: {M}, RX antennas: {N}, TX antennas: {T}")
# This only works with 1 TX antenna (i.e. no MIMO) - see #4 for more details # This only works with 1 TX antenna (i.e. no MIMO) - see #4 for more details

View File

@ -31,6 +31,8 @@ ANTENNA_SPACING = 0.0285 # 2.85 cm
CHANNEL_WIDTH = 20 CHANNEL_WIDTH = 20
FRAME_FORMAT = "HT" FRAME_FORMAT = "HT"
N_SUBCARRIERS = 56
CENTRAL_FREQUENCY_HZ = CENTRAL_FREQUENCY_MHZ * 1_000_000 CENTRAL_FREQUENCY_HZ = CENTRAL_FREQUENCY_MHZ * 1_000_000
C = 299_792_458 # m/s C = 299_792_458 # m/s

View File

@ -1,8 +1,8 @@
import logging import logging
import time import time
import socket import socket
from typing import Callable import multiprocessing as mp
import threading from multiprocessing.connection import Connection
import struct import struct
from datetime import datetime from datetime import datetime
import numpy as np import numpy as np
@ -46,7 +46,7 @@ class FeitReceiver:
f"{__name__}.{self.__class__.__name__}-{self.host}" f"{__name__}.{self.__class__.__name__}-{self.host}"
) )
def listen(self, callback: Callable[[str, CSI], None]): def listen(self, conn: Connection):
prev_time = datetime.now() 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
@ -59,21 +59,26 @@ class FeitReceiver:
f"Received CSI data after {datetime.now() - prev_time}" f"Received CSI data after {datetime.now() - prev_time}"
) )
prev_time = datetime.now() prev_time = datetime.now()
callback(self.host, csidata) conn.send(csidata)
except struct.error: except struct.error:
self.logger.error("Failed to parse CSI data") self.logger.error("Failed to parse CSI data")
class CSIProcessor: class CSIProcessor:
def __init__(self): def __init__(
self, receiver_connections: dict[str, Connection], webserver: Connection
):
self.pending_data: dict[str, tuple[datetime, CSI]] = {} self.pending_data: dict[str, tuple[datetime, CSI]] = {}
self.pending_data_lock = threading.Lock() self.pending_data_lock = mp.Lock()
self.logger = logging.getLogger(f"{__name__}.{self.__class__.__name__}") self.logger = logging.getLogger(f"{__name__}.{self.__class__.__name__}")
self.last_processed = datetime.now() self.last_processed = datetime.now()
self.preprocess = Preprocessor() self.preprocess = Preprocessor()
self.aoa = AoA() self.aoa = AoA()
self.connections = receiver_connections
self.webserver = webserver
def add_data(self, host: str, data: CSI): def add_data(self, host: str, data: CSI):
if ( if (
host in self.pending_data host in self.pending_data
@ -117,9 +122,13 @@ class CSIProcessor:
processed = self.preprocess.preprocess(all_data) processed = self.preprocess.preprocess(all_data)
visualise.add_data(all_data, processed) visualise.add_data(all_data, processed)
self.aoa.update(processed) self.aoa.update(processed)
self.webserver.send(self.aoa.E_n)
def process_forever(self): def process_forever(self):
while True: while True:
for ip, conn in self.connections.items():
if conn.poll():
self.add_data(ip, conn.recv())
if self.is_ready(): if self.is_ready():
self.process_data() self.process_data()
else: else:

View File

@ -5,6 +5,7 @@ import numpy.typing as npt
from simple_websocket import Server from simple_websocket import Server
import time import time
from datetime import datetime from datetime import datetime
from multiprocessing.connection import Connection
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
import io import io
@ -19,6 +20,7 @@ matplotlib.use("agg")
app = Flask(__name__) app = Flask(__name__)
sock = Sock(app) sock = Sock(app)
aoa_conn = None
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -110,11 +112,17 @@ def make_heatmap(max_tof: float):
def gather_aoa(max_tof: float): def gather_aoa(max_tof: float):
assert aoa_conn is not None
prev_frame = datetime.now() prev_frame = datetime.now()
while True: while True:
while (datetime.now() - prev_frame).total_seconds() < 1 / config.HEATMAP_FPS: while (datetime.now() - prev_frame).total_seconds() < 1 / config.HEATMAP_FPS:
time.sleep(0.01) time.sleep(0.01)
pass while aoa_conn.poll():
logger.debug("Receiving from aoa pipe")
E_n = aoa_conn.recv()
aoa.N_subcarriers = 54
aoa.E_n = E_n
prev_frame = datetime.now() prev_frame = datetime.now()
logger.debug("Generating heatmap") logger.debug("Generating heatmap")
buf = make_heatmap(max_tof) buf = make_heatmap(max_tof)
@ -134,5 +142,8 @@ def aoa_tof():
) )
def start(): def start(conn: Connection):
global aoa_conn
aoa_conn = conn
app.run(debug=True, use_reloader=False, host="0.0.0.0") app.run(debug=True, use_reloader=False, host="0.0.0.0")