feat: add cli util for finding correct antenna order
This commit is contained in:
parent
8e1a849b7c
commit
1e23ae063e
21
src/cli.py
21
src/cli.py
@ -1,3 +1,4 @@
|
|||||||
|
import logging
|
||||||
import multiprocessing as mp
|
import multiprocessing as mp
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
@ -10,6 +11,21 @@ from .processing.aoa import AoA
|
|||||||
from .processing.preprocess import Preprocessor
|
from .processing.preprocess import Preprocessor
|
||||||
|
|
||||||
app = typer.Typer()
|
app = typer.Typer()
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@app.command()
|
||||||
|
def antennas() -> None:
|
||||||
|
"""Utility to help determine the order in which antennas are plugged in
|
||||||
|
|
||||||
|
Once the script is running, unplug and replug antennas from left to right, to get
|
||||||
|
the correct order. Every time an antenna is unplugged and replugged, the script will
|
||||||
|
print the antenna identifier. When you are done, press Ctrl+C to stop the script and
|
||||||
|
get the final order.
|
||||||
|
"""
|
||||||
|
from .utils import antenna_order
|
||||||
|
|
||||||
|
antenna_order.main()
|
||||||
|
|
||||||
|
|
||||||
@app.command()
|
@app.command()
|
||||||
@ -22,6 +38,7 @@ def heatmap() -> None:
|
|||||||
webapp.start()
|
webapp.start()
|
||||||
|
|
||||||
def callback(antenna_data: npt.NDArray[np.complex128]) -> None:
|
def callback(antenna_data: npt.NDArray[np.complex128]) -> None:
|
||||||
|
logger.info(f"Got final CSI data with shape {antenna_data.shape}")
|
||||||
processed = preprocessor.preprocess(antenna_data)
|
processed = preprocessor.preprocess(antenna_data)
|
||||||
# visualise.add_data(all_data, processed)
|
# visualise.add_data(all_data, processed)
|
||||||
aoa.update(processed)
|
aoa.update(processed)
|
||||||
@ -29,7 +46,3 @@ def heatmap() -> None:
|
|||||||
webapp_queue.put(aoa)
|
webapp_queue.put(aoa)
|
||||||
|
|
||||||
ingest.start_processing(callback)
|
ingest.start_processing(callback)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
app()
|
|
||||||
|
|||||||
@ -130,14 +130,6 @@ class CSIProcessor:
|
|||||||
with self.pending_data_lock:
|
with self.pending_data_lock:
|
||||||
self.pending_data[host] = (datetime.now(), data)
|
self.pending_data[host] = (datetime.now(), data)
|
||||||
|
|
||||||
# Useful for figuring out the correct antenna order - RSSI values will
|
|
||||||
# decrease when the specific antenna is disconnected
|
|
||||||
rssis = [
|
|
||||||
(ip, csi.header.rssi1, csi.header.rssi2)
|
|
||||||
for ip, (_, csi) in sorted(self.pending_data.items())
|
|
||||||
]
|
|
||||||
self.logger.debug("Antenna RSSI values: {}".format(rssis))
|
|
||||||
|
|
||||||
def is_ready(self) -> bool:
|
def is_ready(self) -> bool:
|
||||||
for host in config.RECEIVE_HOSTS:
|
for host in config.RECEIVE_HOSTS:
|
||||||
if (
|
if (
|
||||||
@ -147,8 +139,16 @@ class CSIProcessor:
|
|||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def process_data(self, callback: CSICallback) -> None:
|
def process_data(
|
||||||
|
self,
|
||||||
|
callback: CSICallback | None = None,
|
||||||
|
pre_merge_callback: Callable[[dict[Host, CSI]], None] | None = None,
|
||||||
|
) -> None:
|
||||||
self.last_processed = datetime.now()
|
self.last_processed = datetime.now()
|
||||||
|
if pre_merge_callback is not None:
|
||||||
|
pre_merge_callback(
|
||||||
|
{host: data[1] for host, data in self.pending_data.items()}
|
||||||
|
)
|
||||||
antenna_data = [
|
antenna_data = [
|
||||||
np.expand_dims(self.pending_data[ip][1].matrix[:, antenna], axis=2)
|
np.expand_dims(self.pending_data[ip][1].matrix[:, antenna], axis=2)
|
||||||
for ip, antenna in config.ANTENNA_ORDER
|
for ip, antenna in config.ANTENNA_ORDER
|
||||||
@ -156,20 +156,21 @@ class CSIProcessor:
|
|||||||
|
|
||||||
# We have data from all servers
|
# We have data from all servers
|
||||||
all_data = np.concat(antenna_data, axis=1)
|
all_data = np.concat(antenna_data, axis=1)
|
||||||
self.logger.info(f"Got final CSI data with shape {all_data.shape}")
|
|
||||||
|
|
||||||
callback(all_data)
|
if callback:
|
||||||
|
callback(all_data)
|
||||||
|
|
||||||
def process_forever(
|
def process_forever(
|
||||||
self,
|
self,
|
||||||
callback: CSICallback,
|
callback: CSICallback | None = None,
|
||||||
|
pre_merge_callback: Callable[[dict[Host, CSI]], None] | None = None,
|
||||||
) -> NoReturn:
|
) -> NoReturn:
|
||||||
while True:
|
while True:
|
||||||
for ip, queue in self.connections.items():
|
for ip, queue in self.connections.items():
|
||||||
while not queue.empty():
|
while not queue.empty():
|
||||||
self.add_data(ip, queue.get())
|
self.add_data(ip, queue.get())
|
||||||
if self.is_ready():
|
if self.is_ready():
|
||||||
self.process_data(callback)
|
self.process_data(callback, pre_merge_callback=pre_merge_callback)
|
||||||
else:
|
else:
|
||||||
self.logger.debug("Not all data is ready")
|
self.logger.debug("Not all data is ready")
|
||||||
time.sleep(0.0005)
|
time.sleep(0.0005)
|
||||||
@ -181,7 +182,10 @@ class Receiver(NamedTuple):
|
|||||||
queue: "mp.Queue[CSI]"
|
queue: "mp.Queue[CSI]"
|
||||||
|
|
||||||
|
|
||||||
def start_processing(csi_callback: CSICallback) -> None:
|
def start_processing(
|
||||||
|
csi_callback: CSICallback | None = None,
|
||||||
|
pre_merge_callback: Callable[[dict[Host, CSI]], None] | None = None,
|
||||||
|
) -> None:
|
||||||
receivers = [
|
receivers = [
|
||||||
Receiver(ip, FeitReceiver(ip), mp.Queue(config.SAMPLE_RATE))
|
Receiver(ip, FeitReceiver(ip), mp.Queue(config.SAMPLE_RATE))
|
||||||
for ip in config.RECEIVE_HOSTS
|
for ip in config.RECEIVE_HOSTS
|
||||||
@ -200,7 +204,7 @@ def start_processing(csi_callback: CSICallback) -> None:
|
|||||||
proc.start()
|
proc.start()
|
||||||
|
|
||||||
processing_thread = mp.Process(
|
processing_thread = mp.Process(
|
||||||
target=processor.process_forever, args=(csi_callback,)
|
target=processor.process_forever, args=(csi_callback, pre_merge_callback)
|
||||||
)
|
)
|
||||||
processing_thread.start()
|
processing_thread.start()
|
||||||
try:
|
try:
|
||||||
|
|||||||
56
src/utils/antenna_order.py
Normal file
56
src/utils/antenna_order.py
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import logging
|
||||||
|
|
||||||
|
from ..collection import ingest
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
AntennaIdentifier = tuple[ingest.Host, int]
|
||||||
|
order: list[AntennaIdentifier] = []
|
||||||
|
prev_unplugged: set[AntennaIdentifier] = set()
|
||||||
|
antenna_average: dict[AntennaIdentifier, float] = {}
|
||||||
|
|
||||||
|
RSSI_THRESHOLD = 8
|
||||||
|
|
||||||
|
|
||||||
|
def callback(antenna_data: dict[ingest.Host, ingest.CSI]) -> None:
|
||||||
|
global prev_unplugged
|
||||||
|
global antenna_average
|
||||||
|
global order
|
||||||
|
unplugged: set[tuple[ingest.Host, int]] = set()
|
||||||
|
logger.debug(
|
||||||
|
"Antenna RSSI: "
|
||||||
|
+ str(
|
||||||
|
{
|
||||||
|
host: (csi.header.rssi1, csi.header.rssi2)
|
||||||
|
for host, csi in antenna_data.items()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
for host, csi in antenna_data.items():
|
||||||
|
antenna_average[(host, 0)] = (
|
||||||
|
antenna_average.get((host, 0), csi.header.rssi1) * 0.9
|
||||||
|
+ csi.header.rssi1 * 0.1
|
||||||
|
)
|
||||||
|
antenna_average[(host, 1)] = (
|
||||||
|
antenna_average.get((host, 1), csi.header.rssi2) * 0.9
|
||||||
|
+ csi.header.rssi2 * 0.1
|
||||||
|
)
|
||||||
|
if csi.header.rssi1 > antenna_average[(host, 0)] + RSSI_THRESHOLD:
|
||||||
|
unplugged.add((host, 0))
|
||||||
|
if csi.header.rssi2 > antenna_average[(host, 1)] + RSSI_THRESHOLD:
|
||||||
|
unplugged.add((host, 1))
|
||||||
|
if prev_unplugged != unplugged:
|
||||||
|
if len(prev_unplugged) > len(unplugged):
|
||||||
|
logger.info(f"Antenna plugged in: {prev_unplugged - unplugged}")
|
||||||
|
else:
|
||||||
|
logger.info(f"Antenna unplugged: {unplugged - prev_unplugged}")
|
||||||
|
diff = list(unplugged - prev_unplugged)
|
||||||
|
if len(diff) == 1 and diff[0] not in order:
|
||||||
|
host, antenna = diff.pop()
|
||||||
|
order.append((host, antenna))
|
||||||
|
logger.info(f"Current order: {order}")
|
||||||
|
prev_unplugged = unplugged
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
ingest.start_processing(pre_merge_callback=callback)
|
||||||
Loading…
Reference in New Issue
Block a user