From 47a32ad369d0eba3cf059a2a7b80d35d354fb346 Mon Sep 17 00:00:00 2001 From: Zachery Aaron Shores-Chmielewski Date: Fri, 21 Aug 2026 23:50:42 +0400 Subject: [PATCH] =?UTF-8?q?Stage=200:=20RSSI=20motion=20logger=20(station-?= =?UTF-8?q?dump=20poll,=20sparkline,=20CSV)=20+=20still-room=20baseline=20?= =?UTF-8?q?(-52dBm=20=C2=B11=20@18.6Hz)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dataset/stage0_baseline.csv | 187 ++++++++++++++++++++++++++++++++++++ tools/stage0/rssi_logger.py | 98 +++++++++++++++++++ 2 files changed, 285 insertions(+) create mode 100644 dataset/stage0_baseline.csv create mode 100755 tools/stage0/rssi_logger.py diff --git a/dataset/stage0_baseline.csv b/dataset/stage0_baseline.csv new file mode 100644 index 0000000..cecab3f --- /dev/null +++ b/dataset/stage0_baseline.csv @@ -0,0 +1,187 @@ +iso_time,monotonic_s,dbm +2026-08-21T23:50:20.262,0.0,-53 +2026-08-21T23:50:20.321,0.056,-53 +2026-08-21T23:50:20.375,0.114,-53 +2026-08-21T23:50:20.430,0.168,-53 +2026-08-21T23:50:20.484,0.222,-53 +2026-08-21T23:50:20.538,0.277,-53 +2026-08-21T23:50:20.591,0.331,-53 +2026-08-21T23:50:20.650,0.385,-53 +2026-08-21T23:50:20.704,0.443,-53 +2026-08-21T23:50:20.762,0.498,-53 +2026-08-21T23:50:20.817,0.555,-53 +2026-08-21T23:50:20.884,0.613,-53 +2026-08-21T23:50:20.942,0.676,-53 +2026-08-21T23:50:20.999,0.734,-53 +2026-08-21T23:50:21.053,0.791,-52 +2026-08-21T23:50:21.106,0.845,-52 +2026-08-21T23:50:21.159,0.898,-53 +2026-08-21T23:50:21.213,0.952,-52 +2026-08-21T23:50:21.268,1.006,-53 +2026-08-21T23:50:21.321,1.06,-52 +2026-08-21T23:50:21.375,1.114,-52 +2026-08-21T23:50:21.428,1.167,-52 +2026-08-21T23:50:21.481,1.221,-53 +2026-08-21T23:50:21.535,1.274,-53 +2026-08-21T23:50:21.590,1.328,-53 +2026-08-21T23:50:21.644,1.382,-53 +2026-08-21T23:50:21.697,1.436,-53 +2026-08-21T23:50:21.756,1.493,-53 +2026-08-21T23:50:21.809,1.548,-53 +2026-08-21T23:50:21.864,1.602,-53 +2026-08-21T23:50:21.917,1.656,-53 +2026-08-21T23:50:21.971,1.71,-53 +2026-08-21T23:50:22.024,1.764,-52 +2026-08-21T23:50:22.080,1.818,-53 +2026-08-21T23:50:22.134,1.873,-53 +2026-08-21T23:50:22.188,1.927,-53 +2026-08-21T23:50:22.242,1.981,-53 +2026-08-21T23:50:22.297,2.034,-53 +2026-08-21T23:50:22.350,2.089,-53 +2026-08-21T23:50:22.403,2.142,-53 +2026-08-21T23:50:22.456,2.196,-53 +2026-08-21T23:50:22.511,2.249,-53 +2026-08-21T23:50:22.564,2.304,-53 +2026-08-21T23:50:22.618,2.357,-53 +2026-08-21T23:50:22.672,2.41,-53 +2026-08-21T23:50:22.726,2.464,-53 +2026-08-21T23:50:22.779,2.518,-53 +2026-08-21T23:50:22.833,2.572,-53 +2026-08-21T23:50:22.887,2.626,-53 +2026-08-21T23:50:22.940,2.679,-53 +2026-08-21T23:50:22.993,2.732,-53 +2026-08-21T23:50:23.046,2.786,-53 +2026-08-21T23:50:23.100,2.838,-53 +2026-08-21T23:50:23.154,2.892,-53 +2026-08-21T23:50:23.207,2.946,-53 +2026-08-21T23:50:23.261,3.0,-53 +2026-08-21T23:50:23.315,3.054,-53 +2026-08-21T23:50:23.368,3.108,-53 +2026-08-21T23:50:23.421,3.161,-52 +2026-08-21T23:50:23.475,3.214,-52 +2026-08-21T23:50:23.530,3.269,-52 +2026-08-21T23:50:23.584,3.323,-52 +2026-08-21T23:50:23.638,3.377,-52 +2026-08-21T23:50:23.692,3.431,-52 +2026-08-21T23:50:23.747,3.485,-53 +2026-08-21T23:50:23.801,3.54,-53 +2026-08-21T23:50:23.854,3.593,-53 +2026-08-21T23:50:23.909,3.647,-53 +2026-08-21T23:50:23.969,3.705,-53 +2026-08-21T23:50:24.024,3.762,-52 +2026-08-21T23:50:24.078,3.816,-52 +2026-08-21T23:50:24.132,3.871,-52 +2026-08-21T23:50:24.186,3.925,-52 +2026-08-21T23:50:24.239,3.978,-52 +2026-08-21T23:50:24.292,4.031,-52 +2026-08-21T23:50:24.346,4.085,-52 +2026-08-21T23:50:24.401,4.139,-52 +2026-08-21T23:50:24.454,4.193,-52 +2026-08-21T23:50:24.507,4.246,-53 +2026-08-21T23:50:24.561,4.3,-53 +2026-08-21T23:50:24.615,4.353,-53 +2026-08-21T23:50:24.671,4.408,-53 +2026-08-21T23:50:24.726,4.463,-53 +2026-08-21T23:50:24.780,4.518,-53 +2026-08-21T23:50:24.833,4.572,-52 +2026-08-21T23:50:24.890,4.625,-52 +2026-08-21T23:50:24.944,4.682,-52 +2026-08-21T23:50:24.998,4.736,-52 +2026-08-21T23:50:25.051,4.791,-52 +2026-08-21T23:50:25.104,4.844,-52 +2026-08-21T23:50:25.158,4.897,-52 +2026-08-21T23:50:25.211,4.95,-52 +2026-08-21T23:50:25.264,5.004,-52 +2026-08-21T23:50:25.317,5.057,-52 +2026-08-21T23:50:25.370,5.11,-53 +2026-08-21T23:50:25.423,5.163,-53 +2026-08-21T23:50:25.476,5.216,-53 +2026-08-21T23:50:25.530,5.269,-53 +2026-08-21T23:50:25.583,5.322,-52 +2026-08-21T23:50:25.636,5.375,-52 +2026-08-21T23:50:25.688,5.428,-53 +2026-08-21T23:50:25.741,5.481,-53 +2026-08-21T23:50:25.794,5.533,-53 +2026-08-21T23:50:25.847,5.586,-53 +2026-08-21T23:50:25.900,5.639,-53 +2026-08-21T23:50:25.953,5.692,-53 +2026-08-21T23:50:26.005,5.745,-53 +2026-08-21T23:50:26.059,5.798,-53 +2026-08-21T23:50:26.111,5.851,-53 +2026-08-21T23:50:26.164,5.904,-53 +2026-08-21T23:50:26.216,5.956,-53 +2026-08-21T23:50:26.269,6.009,-52 +2026-08-21T23:50:26.321,6.061,-52 +2026-08-21T23:50:26.374,6.114,-52 +2026-08-21T23:50:26.426,6.167,-52 +2026-08-21T23:50:26.479,6.219,-52 +2026-08-21T23:50:26.532,6.271,-53 +2026-08-21T23:50:26.584,6.324,-53 +2026-08-21T23:50:26.637,6.377,-53 +2026-08-21T23:50:26.690,6.429,-52 +2026-08-21T23:50:26.743,6.483,-52 +2026-08-21T23:50:26.796,6.535,-52 +2026-08-21T23:50:26.848,6.588,-53 +2026-08-21T23:50:26.901,6.641,-53 +2026-08-21T23:50:26.955,6.694,-53 +2026-08-21T23:50:27.008,6.747,-52 +2026-08-21T23:50:27.063,6.8,-53 +2026-08-21T23:50:27.115,6.855,-53 +2026-08-21T23:50:27.168,6.908,-52 +2026-08-21T23:50:27.221,6.961,-52 +2026-08-21T23:50:27.273,7.013,-52 +2026-08-21T23:50:27.326,7.066,-52 +2026-08-21T23:50:27.379,7.119,-53 +2026-08-21T23:50:27.432,7.171,-53 +2026-08-21T23:50:27.485,7.224,-52 +2026-08-21T23:50:27.538,7.277,-53 +2026-08-21T23:50:27.590,7.33,-53 +2026-08-21T23:50:27.646,7.384,-52 +2026-08-21T23:50:27.699,7.439,-53 +2026-08-21T23:50:27.752,7.491,-52 +2026-08-21T23:50:27.811,7.545,-52 +2026-08-21T23:50:27.866,7.604,-52 +2026-08-21T23:50:27.921,7.658,-52 +2026-08-21T23:50:27.974,7.713,-52 +2026-08-21T23:50:28.027,7.767,-51 +2026-08-21T23:50:28.080,7.82,-51 +2026-08-21T23:50:28.134,7.872,-52 +2026-08-21T23:50:28.187,7.926,-53 +2026-08-21T23:50:28.241,7.98,-52 +2026-08-21T23:50:28.294,8.033,-51 +2026-08-21T23:50:28.351,8.089,-51 +2026-08-21T23:50:28.411,8.145,-51 +2026-08-21T23:50:28.466,8.205,-51 +2026-08-21T23:50:28.519,8.258,-51 +2026-08-21T23:50:28.573,8.312,-52 +2026-08-21T23:50:28.628,8.366,-52 +2026-08-21T23:50:28.682,8.421,-53 +2026-08-21T23:50:28.736,8.475,-52 +2026-08-21T23:50:28.791,8.528,-52 +2026-08-21T23:50:28.845,8.583,-52 +2026-08-21T23:50:28.898,8.638,-52 +2026-08-21T23:50:28.952,8.691,-52 +2026-08-21T23:50:29.005,8.744,-52 +2026-08-21T23:50:29.058,8.797,-52 +2026-08-21T23:50:29.112,8.851,-52 +2026-08-21T23:50:29.166,8.905,-52 +2026-08-21T23:50:29.219,8.958,-52 +2026-08-21T23:50:29.272,9.011,-52 +2026-08-21T23:50:29.324,9.064,-52 +2026-08-21T23:50:29.376,9.117,-52 +2026-08-21T23:50:29.434,9.173,-52 +2026-08-21T23:50:29.486,9.226,-52 +2026-08-21T23:50:29.538,9.278,-52 +2026-08-21T23:50:29.591,9.33,-52 +2026-08-21T23:50:29.643,9.383,-52 +2026-08-21T23:50:29.697,9.436,-52 +2026-08-21T23:50:29.750,9.489,-52 +2026-08-21T23:50:29.803,9.542,-52 +2026-08-21T23:50:29.855,9.595,-52 +2026-08-21T23:50:29.908,9.648,-52 +2026-08-21T23:50:29.961,9.7,-52 +2026-08-21T23:50:30.013,9.753,-51 +2026-08-21T23:50:30.067,9.806,-51 +2026-08-21T23:50:30.120,9.86,-51 +2026-08-21T23:50:30.173,9.913,-51 +2026-08-21T23:50:30.226,9.966,-51 diff --git a/tools/stage0/rssi_logger.py b/tools/stage0/rssi_logger.py new file mode 100755 index 0000000..a31c65e --- /dev/null +++ b/tools/stage0/rssi_logger.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python3 +"""Stage 0 RSSI motion logger (CSI-project-stages.md §0.1). + +Polls the AP's signal strength as fast as `iw` allows and logs +timestamp,dbm to CSV. Bodies moving through the laptop<->AP path wobble +the reading; a still room is a flat line. Busy link (iperf traffic) = +fresh readings. + +Usage: rssi_logger.py [-i wlan0] [-o out.csv] [-d seconds] [--plot] +""" +import argparse +import csv +import datetime +import subprocess +import time +from pathlib import Path + +SPARK = ' ▁▂▃▄▅▆▇█' + + +def poll(iface): + out = subprocess.run(['iw', 'dev', iface, 'station', 'dump'], + capture_output=True, text=True).stdout + for ln in out.splitlines(): + s = ln.strip() + if s.startswith('signal:'): + return int(s.split()[1]) + return None + + +def sparkline(vals): + if not vals: + return '' + lo, hi = min(vals), max(vals) + span = max(1, hi - lo) + return ''.join(SPARK[min(8, 1 + 8 * (v - lo) // span)] for v in vals) + + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument('-i', '--iface', default='wlan0') + ap.add_argument('-o', '--out', default=None) + ap.add_argument('-d', '--duration', type=float, default=0, + help='seconds; 0 = run forever') + ap.add_argument('--plot', action='store_true') + args = ap.parse_args() + + sink = None + if args.out: + Path(args.out).parent.mkdir(parents=True, exist_ok=True) + sink = open(args.out, 'w', newline='') + w = csv.writer(sink) + w.writerow(['iso_time', 'monotonic_s', 'dbm']) + + vals, t0, last_line = [], time.monotonic(), time.monotonic() + n = 0 + try: + while True: + t = time.monotonic() + if args.duration and t - t0 > args.duration: + break + dbm = poll(args.iface) + if dbm is not None: + n += 1 + vals.append(dbm) + if sink: + w.writerow([datetime.datetime.now().isoformat( + timespec='milliseconds'), round(t - t0, 3), dbm]) + if t - last_line >= 1.0: + win = vals[-40:] + spread = (max(win) - min(win)) if win else 0 + mean = sum(win) / len(win) if win else 0 + print(f'\r{sparkline(win[-40:]):<42s} ' + f'now={dbm} mean={mean:.1f} spread={spread}', + end='', flush=True) + last_line = t + time.sleep(0.05) + except KeyboardInterrupt: + pass + finally: + if sink: + sink.close() + print(f'\nsamples={n} rate={n / max(0.001, time.monotonic() - t0):.1f}/s ' + f'min={min(vals)} max={max(vals)}') + if args.plot and vals: + try: + import matplotlib.pyplot as plt + plt.plot(vals) + plt.xlabel('sample') + plt.ylabel('dBm') + plt.title('RSSI') + plt.show() + except ImportError: + print('matplotlib not installed; CSV saved instead') + + +if __name__ == '__main__': + main()