Compare commits
No commits in common. "c4196503d85fbbd31aeeb84d0b28aa8aecca4a0f" and "a94a9b4ec47e52f64d6bff16b8f51642b0ed93be" have entirely different histories.
c4196503d8
...
a94a9b4ec4
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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/
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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:
|
||||
|
||||
Loading…
Reference in New Issue
Block a user