Compare commits

..

No commits in common. "c4196503d85fbbd31aeeb84d0b28aa8aecca4a0f" and "a94a9b4ec47e52f64d6bff16b8f51642b0ed93be" have entirely different histories.

7 changed files with 31 additions and 93 deletions

View File

@ -52,7 +52,7 @@ def heatmap() -> None:
def callback(antenna_data: npt.NDArray[np.complex64]) -> None:
logger.info(f"Got final CSI data with shape {antenna_data.shape}")
visualise_data(antenna_data, visualise.DataType.RAW_CSI)
processed = preprocessor.preprocess(antenna_data, visualiser=visualise_data)
processed = preprocessor.preprocess(antenna_data)
visualise_data(processed, visualise.DataType.PROCESSED_CSI)
logger.info(f"Processed CSI data with shape {processed.shape}")
processed_tensor = torch.tensor(processed, device=device)

View File

@ -1,13 +1,11 @@
import logging
from queue import Queue
from typing import Any, Callable
import numpy as np
import numpy.typing as npt
from scipy.signal import butter, correlate, sosfilt, sosfilt_zi
from ..config import config
from ..visualise import server as visualise
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
@ -28,14 +26,9 @@ class Preprocessor:
output="sos",
)
def preprocess(
self,
h: npt.NDArray[np.complex64],
visualiser: None
| Callable[[npt.NDArray[Any], visualise.DataType], None] = None,
) -> npt.NDArray[np.complex64]:
def preprocess(self, h: npt.NDArray[np.complex64]) -> npt.NDArray[np.complex64]:
# CSI data is not available for pilot subcarriers.
h_hat: npt.NDArray[np.complex64] = np.where(
h_hat = np.where(
np.expand_dims(h[:, 0, 0] == 0, axis=(1, 2)),
correlate(h, [[[1 / 2]], [[0]], [[1 / 2]]], mode="same"),
h,
@ -45,33 +38,12 @@ class Preprocessor:
h_hat = h_hat[:: config.preprocessing.subcarrier_step, :, :]
# logger.info(f"CSI shape: {h_hat.shape}")
# h_hat = np.multiply(h_hat, h_hat.conj() / abs(h_hat.conj()))
h_hat = np.multiply(h_hat, h_hat.conj() / abs(h_hat.conj()))
h_hat = np.nan_to_num(h_hat)
# h_hat = correlate(h_hat, np.ones((3, 1, 1)) / 3)
# h_hat = correlate(h_hat, [[[1 / 4]], [[1 / 2]], [[1 / 4]]], mode="valid")
h_hat = correlate(h_hat, [[[1 / 4]], [[1 / 2]], [[1 / 4]]], mode="valid")
# Unwrap phase and remove linear fit
print(h_hat.shape)
unwrapped = np.unwrap(np.angle(h_hat[:, :, 0]), axis=0).reshape(
h_hat.shape[0], h_hat.shape[1], 1
)
if visualiser:
visualiser(unwrapped, visualise.DataType.UNWRAPPED_PHASE)
for antenna in range(h_hat.shape[1]):
tau, rho = np.linalg.lstsq(
np.vstack([np.arange(h_hat.shape[0]), np.ones(h_hat.shape[0])]).T,
unwrapped[:, antenna, 0],
)[0]
h_hat[:, antenna, 0] = np.abs(h_hat[:, antenna, 0]) * np.exp(
1j
* (
np.angle(h_hat[:, antenna, 0])
- (tau * np.arange(h_hat.shape[0]) + rho)
)
)
return h_hat
# Assume that all csi matrices will have the same shape
if self.long_term_avg.shape != h_hat.shape:
self.long_term_avg = np.zeros(h_hat.shape, dtype=np.complex64)

View File

@ -11,13 +11,13 @@ AntennaIdentifier = tuple[ingest.Host, int]
order: list[AntennaIdentifier] = []
prev_unplugged: set[AntennaIdentifier] = set()
long_antenna_average: dict[AntennaIdentifier, deque[int]] = {
(host, 0): deque(maxlen=15 * config.sample_rate) for host in config.receive_hosts
} | {(host, 1): deque(maxlen=15 * config.sample_rate) for host in config.receive_hosts}
(host, 0): deque(maxlen=10 * config.sample_rate) for host in config.receive_hosts
} | {(host, 1): deque(maxlen=10 * config.sample_rate) for host in config.receive_hosts}
short_antenna_average: dict[AntennaIdentifier, deque[int]] = {
(host, 0): deque(maxlen=2 * config.sample_rate) for host in config.receive_hosts
} | {(host, 1): deque(maxlen=2 * config.sample_rate) for host in config.receive_hosts}
(host, 0): deque(maxlen=config.sample_rate) for host in config.receive_hosts
} | {(host, 1): deque(maxlen=config.sample_rate) for host in config.receive_hosts}
RSSI_THRESHOLD = 10
RSSI_THRESHOLD = 8
def average(data: Collection[Any]) -> float:

View File

@ -6,5 +6,4 @@ server/generated: protos/*.proto
find protos/ -type f -name "*.proto" | xargs uv run protol --create-package --in-place --python-out server/generated/ protoc --proto-path=protos/
frontend/src/grpc: protos/*.proto
mkdir -p frontend/src/grpc
cd frontend && find ../protos/ -name "*.proto" | xargs npx protoc --ts_out=src/grpc -I../protos/
find protos/ -name "*.proto" | xargs npm exec --prefix frontend/ protoc --ts_out frontend/src/grpc -I protos/

View File

@ -11,18 +11,6 @@ import { computed } from 'vue'
const { figure, paused = false } = defineProps<{ figure: Figure; paused?: boolean }>()
const cancel = ref<boolean>(false)
function reshape(data: number[], width: number) {
if (data.length % width != 0) {
throw new Error('Data length is not divisible by width')
}
const height = data.length / width
const result: number[][] = new Array(height)
for (let i = 0; i < height; i++) {
result[i] = data.slice(i * width, (i + 1) * width)
}
return result
}
const data = ref<FigureData | null>(null)
const plotData = computed(() => {
if (data.value) {
@ -36,12 +24,16 @@ const plotData = computed(() => {
color: line.color ? line.color : undefined,
}))
case 'heatmap':
return [
{
z: reshape(data.value.figure.heatmap.data, data.value.figure.heatmap.width),
type: 'heatmap' as const,
},
]
return []
/* data.value.figure.heatmap.lines.map((line) => {
return {
x: line.x,
y: line.y,
type: 'scatter',
name: line.label,
color: line.color,
}
})*/
}
}
return []
@ -51,7 +43,6 @@ const layout = {
title: { text: figure.title },
xaxis: { title: { text: figure.xLabel } },
yaxis: { title: { text: figure.yLabel } },
height: 700,
}
function updateGraph() {

View File

@ -2,8 +2,9 @@ syntax = "proto3";
message HeatmapData {
string uuid = 1;
repeated float data = 2;
uint32 width = 3;
uint32 height = 4;
string cmap = 5;
repeated float x = 2;
repeated float y = 3;
uint32 width = 4;
uint32 height = 5;
string cmap = 6;
}

View File

@ -23,9 +23,8 @@ clients_lock = threading.Lock()
class DataType(Enum):
RAW_CSI = 1
UNWRAPPED_PHASE = 2
PROCESSED_CSI = 3
HEATMAP = 4
PROCESSED_CSI = 2
HEATMAP = 3
@dataclass
@ -41,12 +40,6 @@ figures = {
x_label="Subcarrier",
y_label="Phase",
),
"unwrapped_phase": figure_pb2.Figure(
uuid=str(uuid.uuid4()),
title="Unwrapped CSI Phase",
x_label="Subcarrier",
y_label="Unwrapped phase",
),
"processed_phase": figure_pb2.Figure(
uuid=str(uuid.uuid4()),
title="Preprocessed CSI Phase",
@ -55,9 +48,9 @@ figures = {
),
"aoa_heatmap": figure_pb2.Figure(
uuid=str(uuid.uuid4()),
title="AoA Heatmap",
x_label="ToF",
y_label="AoA",
title="Preprocessed CSI Phase",
x_label="Subcarrier",
y_label="Phase",
),
}
@ -111,25 +104,7 @@ def add_data(dtype: DataType, new_data: npt.NDArray[np.complex128]) -> None:
for q in clients.get(uuid, []):
q.put(figure_pb2.FigureData(uuid=uuid, line=linechart))
case DataType.HEATMAP:
uuid = figures["aoa_heatmap"].uuid
heatmap = heatmap_pb2.HeatmapData(
uuid=uuid,
data=new_data.flatten(),
width=new_data.shape[1],
height=new_data.shape[0],
)
for q in clients.get(uuid, []):
q.put(figure_pb2.FigureData(uuid=uuid, heatmap=heatmap))
pass
case DataType.UNWRAPPED_PHASE:
uuid = figures["unwrapped_phase"].uuid
lines = [
line_pb2.LineChartData.Line(y=new_data[:, i, 0], label=f"Antenna {i}")
for i in range(new_data.shape[1])
]
linechart = line_pb2.LineChartData(lines=lines)
for q in clients.get(uuid, []):
q.put(figure_pb2.FigureData(uuid=uuid, line=linechart))
def listen_for_data(data_queue: "mp.Queue[VisualiserData]") -> None: