Allow replaying traffic from file

This commit is contained in:
Christos Falas 2025-01-27 14:56:44 +00:00
parent 659525cefb
commit 1c4bf3e340
No known key found for this signature in database
8 changed files with 91 additions and 25 deletions

View File

@ -6,13 +6,12 @@ import numpy.typing as npt
import typer import typer
from .. import visualise from .. import visualise
from ..collection import ingest
from ..config import config from ..config import config
from ..processing.aoa import AoA from ..processing.aoa import AoA
from ..processing.preprocess import Preprocessor from ..processing.preprocess import Preprocessor
from . import file from . import file, globals
app = typer.Typer() app = typer.Typer(callback=globals.main)
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -27,6 +26,9 @@ def antennas() -> None:
""" """
from ..utils import antenna_order from ..utils import antenna_order
if not globals.is_live:
raise ValueError("This command only works with live data")
antenna_order.main() antenna_order.main()
@ -48,7 +50,8 @@ def heatmap() -> None:
if not webapp_queue.full(): if not webapp_queue.full():
webapp_queue.put(aoa) webapp_queue.put(aoa)
ingest.start_processing(callback) globals.csi_producer(csi_callback=callback)
logger.info("Finished processing CSI data")
app.add_typer(file.app, name="file", help="Commands for working with CSI files") app.add_typer(file.app, name="file", help="Commands for working with CSI files")

View File

@ -7,7 +7,8 @@ import numpy as np
import numpy.typing as npt import numpy.typing as npt
import typer import typer
from ..collection import ingest from . import globals
app = typer.Typer() app = typer.Typer()
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -24,17 +25,4 @@ def capture(output_path: Path) -> None:
logger.info(f"Got final CSI data with shape {antenna_data.shape}") logger.info(f"Got final CSI data with shape {antenna_data.shape}")
file.create_dataset(datetime.now().isoformat(), data=antenna_data) file.create_dataset(datetime.now().isoformat(), data=antenna_data)
ingest.start_processing(callback) globals.csi_producer(csi_callback=callback)
@app.command()
def replay(input_path: Path) -> None:
"""Replay CSI data from a file"""
logger.info(f"Replaying data from {input_path}")
start_time = datetime.now()
with h5py.File(input_path, "r") as file:
for key in file:
logger.info(f"Sending data from {key}")
ingest.send_data(file[key][:])

18
src/cli/globals.py Normal file
View File

@ -0,0 +1,18 @@
from functools import partial
from pathlib import Path
from .. import collection
from ..collection import file, ingest
csi_producer: collection.CSIProducer = collection.noop
is_live = True
def main(from_file: Path | None = None) -> None:
global csi_producer, is_live
if from_file:
csi_producer = partial(file.start_processing, file_path=from_file)
is_live = False
else:
csi_producer = ingest.start_processing
is_live = True

View File

@ -0,0 +1,9 @@
from . import file, ingest
from .protocols import CSICallback, CSIProducer
def noop(csi_callback: CSICallback | None = None) -> None:
del csi_callback
__all__ = ["file", "ingest", "noop", "CSIProducer", "CSICallback"]

39
src/collection/file.py Normal file
View File

@ -0,0 +1,39 @@
import logging
import time
from datetime import datetime, timedelta
from pathlib import Path
import h5py
from . import protocols
logger = logging.getLogger(__name__)
def start_processing(
file_path: Path,
csi_callback: protocols.CSICallback | None = None,
) -> None:
logger.info(f"Replaying CSI data from {file_path}")
with h5py.File(file_path, "r") as file:
try:
for key in file:
datetime.fromisoformat(key)
except ValueError as e:
logger.exception(
"The file provided was not generated using this software", e
)
start_time = datetime.fromisoformat(list(file.keys())[0])
target_offset = datetime.now() - start_time
for key in file:
logger.debug(f"Sending data from {key} at {datetime.now().isoformat()}")
csi_callback(file[key][:])
curr_time_virtual = datetime.fromisoformat(key)
new_offset = datetime.now() - curr_time_virtual
logger.debug(f"New offset {new_offset}, target is {target_offset}")
if new_offset > target_offset + timedelta(seconds=1):
logger.warning(
f"Data is {new_offset - target_offset} behind, lagging behind..."
)
time.sleep(max(0, (target_offset - new_offset).total_seconds()))

View File

@ -9,13 +9,12 @@ from datetime import datetime
from typing import Callable, NamedTuple from typing import Callable, NamedTuple
import numpy as np import numpy as np
import numpy.typing as npt
from ..config import config from ..config import config
from .csi_frame import CSI from .csi_frame import CSI
from .protocols import CSICallback
Host = tuple[str, int] Host = tuple[str, int]
CSICallback = Callable[[npt.NDArray[np.complex128]], None]
class FeitHost: class FeitHost:

View File

@ -0,0 +1,10 @@
from typing import Callable, Protocol
import numpy as np
import numpy.typing as npt
CSICallback = Callable[[npt.NDArray[np.complex128]], None]
class CSIProducer(Protocol):
def __call__(self, csi_callback: CSICallback | None = None) -> None: ...

View File

@ -28,12 +28,12 @@ def callback(antenna_data: dict[ingest.Host, ingest.CSI]) -> None:
) )
for host, csi in antenna_data.items(): for host, csi in antenna_data.items():
antenna_average[(host, 0)] = ( antenna_average[(host, 0)] = (
antenna_average.get((host, 0), csi.header.rssi1) * 0.9 antenna_average.get((host, 0), csi.header.rssi1) * 0.99
+ csi.header.rssi1 * 0.1 + csi.header.rssi1 * 0.01
) )
antenna_average[(host, 1)] = ( antenna_average[(host, 1)] = (
antenna_average.get((host, 1), csi.header.rssi2) * 0.9 antenna_average.get((host, 1), csi.header.rssi2) * 0.99
+ csi.header.rssi2 * 0.1 + csi.header.rssi2 * 0.01
) )
if csi.header.rssi1 > antenna_average[(host, 0)] + RSSI_THRESHOLD: if csi.header.rssi1 > antenna_average[(host, 0)] + RSSI_THRESHOLD:
unplugged.add((host, 0)) unplugged.add((host, 0))