mirror of
https://github.com/ruvnet/RuView
synced 2026-06-09 10:13:17 +00:00
research(R6): Fresnel-zone forward model — bedrock physics for CSI sensitivity (#710)
The workspace DSP (vital_signs, multistatic, pose_tracker, tomography) implicitly assumes a forward model that maps scatterer geometry to per-subcarrier phase shifts. Nobody had written it down. This tick makes it explicit. Closed-form first-Fresnel-zone radius + point-scatterer path-delta + per-subcarrier phase prediction over 802.11n/ac 20 MHz channels (52 subcarriers, 312.5 kHz spacing). Pure NumPy demo + JSON output for downstream consumers. Headline numbers: - 5 m link first-Fresnel radius @ midpoint: 40 cm (2.4 GHz), 27 cm (5 GHz) - Inside zone-1: phase spread <0.5 deg across 52 subcarriers (band-flat) - Outside zone-1: phase spread up to 16 deg (band-dispersed) This unifies R5 + R6: R5's experimentally measured band-spread top subcarriers is exactly what the Fresnel forward model predicts for zone-1 occupancy. Closes the loop on three earlier threads: - R7 (mincut adversarial) gets a precise definition of 'physically inconsistent' instead of a learned classifier - R10 (foliage range) needs to retract 100 m sparse estimate to ~70 m to account for Fresnel-zone obstruction - R12 (eigenshift negative result) gets its revision basis: PABS over Fresnel-grounded forward operator Honest scope: point-scatterer only, first Fresnel only, frequency-flat reflectivity, LOS-only (no multipath). The scalar version is the right first-order approximation; volume-integral / multi-zone / multipath extensions catalogued as R6.1+R6.2 follow-ups. Coordination: ticks/tick-8.md, no PROGRESS.md edit.
This commit is contained in:
@@ -0,0 +1,586 @@
|
||||
{
|
||||
"model": "first-Fresnel-zone ellipsoid + per-subcarrier path-delta forward model",
|
||||
"constants": {
|
||||
"c_mps": 299800000.0
|
||||
},
|
||||
"scenarios": [
|
||||
{
|
||||
"name": "human-standing-at-midpoint",
|
||||
"link_m": 5.0,
|
||||
"scatterer_offset_m": 0.1,
|
||||
"scatterer_position_m": 2.5,
|
||||
"freq_2.4_GHz": {
|
||||
"first_fresnel_radius_m": 0.39515292398428903,
|
||||
"zone": "zone-1",
|
||||
"path_delta_m": 0.003998401278721531,
|
||||
"phase_rad_per_subcarrier": [
|
||||
0.20043478616963525,
|
||||
0.2004609731027643,
|
||||
0.20048716003589334,
|
||||
0.20051334696902237,
|
||||
0.2005395339021514,
|
||||
0.20056572083528046,
|
||||
0.2005919077684095,
|
||||
0.20061809470153852,
|
||||
0.20064428163466755,
|
||||
0.2006704685677966,
|
||||
0.2006966555009256,
|
||||
0.20072284243405467,
|
||||
0.2007490293671837,
|
||||
0.2007752163003127,
|
||||
0.20080140323344178,
|
||||
0.2008275901665708,
|
||||
0.20085377709969982,
|
||||
0.20087996403282884,
|
||||
0.20090615096595793,
|
||||
0.20093233789908693,
|
||||
0.20095852483221596,
|
||||
0.20098471176534502,
|
||||
0.20101089869847405,
|
||||
0.20103708563160308,
|
||||
0.20106327256473214,
|
||||
0.20108945949786114,
|
||||
0.2011156464309902,
|
||||
0.20114183336411923,
|
||||
0.20116802029724826,
|
||||
0.2011942072303773,
|
||||
0.20122039416350632,
|
||||
0.20124658109663537,
|
||||
0.2012727680297644,
|
||||
0.20129895496289343,
|
||||
0.20132514189602246,
|
||||
0.20135132882915152,
|
||||
0.20137751576228052,
|
||||
0.20140370269540958,
|
||||
0.2014298896285386,
|
||||
0.20145607656166764,
|
||||
0.20148226349479667,
|
||||
0.20150845042792573,
|
||||
0.20153463736105473,
|
||||
0.20156082429418376,
|
||||
0.20158701122731285,
|
||||
0.20161319816044185,
|
||||
0.20163938509357088,
|
||||
0.20166557202669994,
|
||||
0.20169175895982897,
|
||||
0.201717945892958,
|
||||
0.20174413282608702,
|
||||
0.20177031975921605
|
||||
],
|
||||
"phase_rad_min": 0.20043478616963525,
|
||||
"phase_rad_max": 0.20177031975921605,
|
||||
"phase_rad_spread": 0.0013355335895808007,
|
||||
"phase_wraps": 0
|
||||
},
|
||||
"freq_5.0_GHz": {
|
||||
"first_fresnel_radius_m": 0.27376997644007645,
|
||||
"zone": "zone-1",
|
||||
"path_delta_m": 0.003998401278721531,
|
||||
"phase_rad_per_subcarrier": [
|
||||
0.41831006980320795,
|
||||
0.41833625673633695,
|
||||
0.41836244366946607,
|
||||
0.41838863060259507,
|
||||
0.4184148175357241,
|
||||
0.4184410044688532,
|
||||
0.4184671914019822,
|
||||
0.4184933783351112,
|
||||
0.4185195652682403,
|
||||
0.4185457522013693,
|
||||
0.4185719391344983,
|
||||
0.41859812606762736,
|
||||
0.41862431300075637,
|
||||
0.4186504999338854,
|
||||
0.4186766868670145,
|
||||
0.41870287380014354,
|
||||
0.41872906073327254,
|
||||
0.4187552476664016,
|
||||
0.4187814345995306,
|
||||
0.4188076215326596,
|
||||
0.41883380846578866,
|
||||
0.4188599953989178,
|
||||
0.4188861823320468,
|
||||
0.4189123692651758,
|
||||
0.41893855619830483,
|
||||
0.41896474313143384,
|
||||
0.4189909300645629,
|
||||
0.4190171169976919,
|
||||
0.41904330393082095,
|
||||
0.41906949086395,
|
||||
0.41909567779707907,
|
||||
0.41912186473020807,
|
||||
0.4191480516633371,
|
||||
0.41917423859646613,
|
||||
0.41920042552959513,
|
||||
0.4192266124627242,
|
||||
0.41925279939585325,
|
||||
0.41927898632898225,
|
||||
0.4193051732621113,
|
||||
0.41933136019524037,
|
||||
0.41935754712836937,
|
||||
0.4193837340614984,
|
||||
0.4194099209946275,
|
||||
0.4194361079277565,
|
||||
0.4194622948608855,
|
||||
0.41948848179401454,
|
||||
0.41951466872714355,
|
||||
0.4195408556602726,
|
||||
0.4195670425934017,
|
||||
0.4195932295265307,
|
||||
0.4196194164596597,
|
||||
0.4196456033927888
|
||||
],
|
||||
"phase_rad_min": 0.41831006980320795,
|
||||
"phase_rad_max": 0.4196456033927888,
|
||||
"phase_rad_spread": 0.0013355335895808285,
|
||||
"phase_wraps": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "human-walking-into-fresnel",
|
||||
"link_m": 5.0,
|
||||
"scatterer_offset_m": 0.25,
|
||||
"scatterer_position_m": 2.5,
|
||||
"freq_2.4_GHz": {
|
||||
"first_fresnel_radius_m": 0.39515292398428903,
|
||||
"zone": "zone-1",
|
||||
"path_delta_m": 0.024937810560444973,
|
||||
"phase_rad_per_subcarrier": [
|
||||
1.2501008225017065,
|
||||
1.2502641489744661,
|
||||
1.2504274754472258,
|
||||
1.2505908019199852,
|
||||
1.250754128392745,
|
||||
1.2509174548655044,
|
||||
1.2510807813382638,
|
||||
1.2512441078110232,
|
||||
1.251407434283783,
|
||||
1.2515707607565425,
|
||||
1.2517340872293021,
|
||||
1.2518974137020618,
|
||||
1.2520607401748214,
|
||||
1.2522240666475808,
|
||||
1.2523873931203406,
|
||||
1.2525507195930998,
|
||||
1.2527140460658595,
|
||||
1.2528773725386189,
|
||||
1.2530406990113787,
|
||||
1.2532040254841381,
|
||||
1.2533673519568977,
|
||||
1.2535306784296574,
|
||||
1.253694004902417,
|
||||
1.2538573313751764,
|
||||
1.254020657847936,
|
||||
1.2541839843206957,
|
||||
1.254347310793455,
|
||||
1.2545106372662147,
|
||||
1.2546739637389743,
|
||||
1.254837290211734,
|
||||
1.2550006166844934,
|
||||
1.2551639431572532,
|
||||
1.2553272696300126,
|
||||
1.2554905961027722,
|
||||
1.2556539225755317,
|
||||
1.2558172490482913,
|
||||
1.2559805755210507,
|
||||
1.2561439019938105,
|
||||
1.25630722846657,
|
||||
1.2564705549393296,
|
||||
1.256633881412089,
|
||||
1.2567972078848488,
|
||||
1.2569605343576082,
|
||||
1.2571238608303679,
|
||||
1.2572871873031273,
|
||||
1.257450513775887,
|
||||
1.2576138402486463,
|
||||
1.2577771667214062,
|
||||
1.2579404931941656,
|
||||
1.2581038196669252,
|
||||
1.2582671461396846,
|
||||
1.2584304726124445
|
||||
],
|
||||
"phase_rad_min": 1.2501008225017065,
|
||||
"phase_rad_max": 1.2584304726124445,
|
||||
"phase_rad_spread": 0.00832965011073794,
|
||||
"phase_wraps": 0
|
||||
},
|
||||
"freq_5.0_GHz": {
|
||||
"first_fresnel_radius_m": 0.27376997644007645,
|
||||
"zone": "zone-1",
|
||||
"path_delta_m": 0.024937810560444973,
|
||||
"phase_rad_per_subcarrier": [
|
||||
2.608977075861283,
|
||||
2.609140402334042,
|
||||
2.609303728806802,
|
||||
2.609467055279562,
|
||||
2.6096303817523214,
|
||||
2.6097937082250806,
|
||||
2.6099570346978402,
|
||||
2.6101203611706,
|
||||
2.6102836876433595,
|
||||
2.610447014116119,
|
||||
2.6106103405888783,
|
||||
2.6107736670616384,
|
||||
2.6109369935343976,
|
||||
2.611100320007157,
|
||||
2.611263646479917,
|
||||
2.611426972952677,
|
||||
2.611590299425436,
|
||||
2.6117536258981953,
|
||||
2.6119169523709553,
|
||||
2.6120802788437145,
|
||||
2.612243605316474,
|
||||
2.6124069317892338,
|
||||
2.6125702582619934,
|
||||
2.612733584734753,
|
||||
2.6128969112075127,
|
||||
2.613060237680272,
|
||||
2.613223564153032,
|
||||
2.613386890625791,
|
||||
2.6135502170985507,
|
||||
2.6137135435713104,
|
||||
2.61387687004407,
|
||||
2.6140401965168296,
|
||||
2.614203522989589,
|
||||
2.6143668494623484,
|
||||
2.614530175935108,
|
||||
2.6146935024078677,
|
||||
2.6148568288806273,
|
||||
2.6150201553533865,
|
||||
2.6151834818261466,
|
||||
2.6153468082989058,
|
||||
2.6155101347716654,
|
||||
2.615673461244425,
|
||||
2.615836787717185,
|
||||
2.6160001141899443,
|
||||
2.616163440662704,
|
||||
2.616326767135463,
|
||||
2.616490093608223,
|
||||
2.6166534200809823,
|
||||
2.616816746553742,
|
||||
2.6169800730265016,
|
||||
2.6171433994992612,
|
||||
2.617306725972021
|
||||
],
|
||||
"phase_rad_min": 2.608977075861283,
|
||||
"phase_rad_max": 2.617306725972021,
|
||||
"phase_rad_spread": 0.00832965011073794,
|
||||
"phase_wraps": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "scatterer-outside-fresnel",
|
||||
"link_m": 5.0,
|
||||
"scatterer_offset_m": 1.5,
|
||||
"scatterer_position_m": 2.5,
|
||||
"freq_2.4_GHz": {
|
||||
"first_fresnel_radius_m": 0.39515292398428903,
|
||||
"zone": "far-field",
|
||||
"path_delta_m": 0.8309518948453007,
|
||||
"phase_rad_per_subcarrier": [
|
||||
41.65456484993552,
|
||||
41.660007045499924,
|
||||
41.66544924106432,
|
||||
41.67089143662873,
|
||||
41.676333632193135,
|
||||
41.681775827757534,
|
||||
41.68721802332193,
|
||||
41.69266021888634,
|
||||
41.69810241445074,
|
||||
41.703544610015136,
|
||||
41.70898680557954,
|
||||
41.71442900114395,
|
||||
41.71987119670835,
|
||||
41.72531339227275,
|
||||
41.73075558783716,
|
||||
41.73619778340156,
|
||||
41.74163997896596,
|
||||
41.74708217453036,
|
||||
41.75252437009476,
|
||||
41.75796656565916,
|
||||
41.763408761223566,
|
||||
41.76885095678797,
|
||||
41.77429315235237,
|
||||
41.77973534791677,
|
||||
41.78517754348118,
|
||||
41.79061973904558,
|
||||
41.79606193460998,
|
||||
41.801504130174386,
|
||||
41.806946325738785,
|
||||
41.812388521303184,
|
||||
41.81783071686759,
|
||||
41.823272912431996,
|
||||
41.828715107996395,
|
||||
41.834157303560794,
|
||||
41.83959949912521,
|
||||
41.845041694689606,
|
||||
41.850483890254004,
|
||||
41.85592608581841,
|
||||
41.86136828138281,
|
||||
41.86681047694721,
|
||||
41.87225267251161,
|
||||
41.87769486807602,
|
||||
41.88313706364042,
|
||||
41.88857925920482,
|
||||
41.89402145476923,
|
||||
41.89946365033363,
|
||||
41.90490584589803,
|
||||
41.91034804146243,
|
||||
41.91579023702683,
|
||||
41.92123243259123,
|
||||
41.92667462815563,
|
||||
41.932116823720044
|
||||
],
|
||||
"phase_rad_min": 41.65456484993552,
|
||||
"phase_rad_max": 41.932116823720044,
|
||||
"phase_rad_spread": 0.2775519737845258,
|
||||
"phase_wraps": 0
|
||||
},
|
||||
"freq_5.0_GHz": {
|
||||
"first_fresnel_radius_m": 0.27376997644007645,
|
||||
"zone": "far-field",
|
||||
"path_delta_m": 0.8309518948453007,
|
||||
"phase_rad_per_subcarrier": [
|
||||
86.933631945763,
|
||||
86.9390741413274,
|
||||
86.94451633689181,
|
||||
86.94995853245621,
|
||||
86.9554007280206,
|
||||
86.96084292358502,
|
||||
86.9662851191494,
|
||||
86.97172731471382,
|
||||
86.97716951027823,
|
||||
86.98261170584261,
|
||||
86.98805390140703,
|
||||
86.99349609697143,
|
||||
86.99893829253583,
|
||||
87.00438048810022,
|
||||
87.00982268366464,
|
||||
87.01526487922904,
|
||||
87.02070707479345,
|
||||
87.02614927035783,
|
||||
87.03159146592225,
|
||||
87.03703366148665,
|
||||
87.04247585705103,
|
||||
87.04791805261546,
|
||||
87.05336024817986,
|
||||
87.05880244374426,
|
||||
87.06424463930865,
|
||||
87.06968683487307,
|
||||
87.07512903043745,
|
||||
87.08057122600187,
|
||||
87.08601342156628,
|
||||
87.09145561713066,
|
||||
87.09689781269508,
|
||||
87.10234000825947,
|
||||
87.10778220382387,
|
||||
87.11322439938827,
|
||||
87.11866659495267,
|
||||
87.12410879051708,
|
||||
87.1295509860815,
|
||||
87.13499318164588,
|
||||
87.14043537721028,
|
||||
87.1458775727747,
|
||||
87.15131976833908,
|
||||
87.1567619639035,
|
||||
87.1622041594679,
|
||||
87.1676463550323,
|
||||
87.1730885505967,
|
||||
87.17853074616112,
|
||||
87.1839729417255,
|
||||
87.18941513728991,
|
||||
87.19485733285431,
|
||||
87.20029952841871,
|
||||
87.20574172398312,
|
||||
87.21118391954751
|
||||
],
|
||||
"phase_rad_min": 86.933631945763,
|
||||
"phase_rad_max": 87.21118391954751,
|
||||
"phase_rad_spread": 0.2775519737845116,
|
||||
"phase_wraps": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "scatterer-near-Tx",
|
||||
"link_m": 5.0,
|
||||
"scatterer_offset_m": 0.05,
|
||||
"scatterer_position_m": 0.5,
|
||||
"freq_2.4_GHz": {
|
||||
"first_fresnel_radius_m": 0.23709175439057345,
|
||||
"zone": "zone-1",
|
||||
"path_delta_m": 0.002771550260963096,
|
||||
"phase_rad_per_subcarrier": [
|
||||
0.13893430028417714,
|
||||
0.13895245213945337,
|
||||
0.13897060399472957,
|
||||
0.13898875585000578,
|
||||
0.139006907705282,
|
||||
0.13902505956055825,
|
||||
0.13904321141583445,
|
||||
0.13906136327111066,
|
||||
0.1390795151263869,
|
||||
0.1390976669816631,
|
||||
0.13911581883693933,
|
||||
0.13913397069221556,
|
||||
0.13915212254749174,
|
||||
0.13917027440276797,
|
||||
0.1391884262580442,
|
||||
0.1392065781133204,
|
||||
0.13922472996859664,
|
||||
0.13924288182387284,
|
||||
0.13926103367914908,
|
||||
0.13927918553442528,
|
||||
0.13929733738970151,
|
||||
0.13931548924497772,
|
||||
0.13933364110025395,
|
||||
0.13935179295553016,
|
||||
0.1393699448108064,
|
||||
0.1393880966660826,
|
||||
0.13940624852135883,
|
||||
0.13942440037663503,
|
||||
0.13944255223191127,
|
||||
0.13946070408718747,
|
||||
0.13947885594246368,
|
||||
0.1394970077977399,
|
||||
0.13951515965301614,
|
||||
0.13953331150829235,
|
||||
0.13955146336356858,
|
||||
0.1395696152188448,
|
||||
0.139587767074121,
|
||||
0.13960591892939725,
|
||||
0.13962407078467345,
|
||||
0.13964222263994966,
|
||||
0.13966037449522586,
|
||||
0.13967852635050212,
|
||||
0.1396966782057783,
|
||||
0.13971483006105453,
|
||||
0.13973298191633077,
|
||||
0.13975113377160697,
|
||||
0.13976928562688318,
|
||||
0.1397874374821594,
|
||||
0.13980558933743562,
|
||||
0.13982374119271185,
|
||||
0.13984189304798808,
|
||||
0.13986004490326429
|
||||
],
|
||||
"phase_rad_min": 0.13893430028417714,
|
||||
"phase_rad_max": 0.13986004490326429,
|
||||
"phase_rad_spread": 0.0009257446190871488,
|
||||
"phase_wraps": 0
|
||||
},
|
||||
"freq_5.0_GHz": {
|
||||
"first_fresnel_radius_m": 0.16426198586404586,
|
||||
"zone": "zone-1",
|
||||
"path_delta_m": 0.002771550260963096,
|
||||
"phase_rad_per_subcarrier": [
|
||||
0.28995773618231585,
|
||||
0.28997588803759206,
|
||||
0.2899940398928683,
|
||||
0.2900121917481445,
|
||||
0.2900303436034208,
|
||||
0.29004849545869693,
|
||||
0.29006664731397314,
|
||||
0.29008479916924934,
|
||||
0.29010295102452566,
|
||||
0.29012110287980186,
|
||||
0.29013925473507807,
|
||||
0.2901574065903542,
|
||||
0.2901755584456305,
|
||||
0.2901937103009067,
|
||||
0.2902118621561829,
|
||||
0.29023001401145915,
|
||||
0.2902481658667354,
|
||||
0.29026631772201156,
|
||||
0.29028446957728776,
|
||||
0.290302621432564,
|
||||
0.29032077328784023,
|
||||
0.2903389251431165,
|
||||
0.2903570769983927,
|
||||
0.2903752288536689,
|
||||
0.2903933807089451,
|
||||
0.2904115325642213,
|
||||
0.2904296844194975,
|
||||
0.2904478362747738,
|
||||
0.29046598813005,
|
||||
0.2904841399853262,
|
||||
0.2905022918406024,
|
||||
0.29052044369587865,
|
||||
0.29053859555115485,
|
||||
0.29055674740643106,
|
||||
0.29057489926170726,
|
||||
0.2905930511169835,
|
||||
0.29061120297225973,
|
||||
0.29062935482753594,
|
||||
0.2906475066828122,
|
||||
0.2906656585380884,
|
||||
0.2906838103933646,
|
||||
0.2907019622486408,
|
||||
0.29072011410391707,
|
||||
0.2907382659591933,
|
||||
0.2907564178144695,
|
||||
0.2907745696697457,
|
||||
0.2907927215250219,
|
||||
0.2908108733802981,
|
||||
0.29082902523557436,
|
||||
0.29084717709085056,
|
||||
0.2908653289461268,
|
||||
0.290883480801403
|
||||
],
|
||||
"phase_rad_min": 0.28995773618231585,
|
||||
"phase_rad_max": 0.290883480801403,
|
||||
"phase_rad_spread": 0.0009257446190871765,
|
||||
"phase_wraps": 0
|
||||
}
|
||||
}
|
||||
],
|
||||
"first_fresnel_radii_m": {
|
||||
"2.4": {
|
||||
"wavelength_mm": 124.91666666666666,
|
||||
"link_2.0m": {
|
||||
"p=0.10": 0.14994999166388773,
|
||||
"p=0.25": 0.2164341701303193,
|
||||
"p=0.50": 0.2499166527731462,
|
||||
"p=0.75": 0.2164341701303193,
|
||||
"p=0.90": 0.1499499916638877
|
||||
},
|
||||
"link_5.0m": {
|
||||
"p=0.10": 0.23709175439057345,
|
||||
"p=0.25": 0.3422124705500955,
|
||||
"p=0.50": 0.39515292398428903,
|
||||
"p=0.75": 0.3422124705500955,
|
||||
"p=0.90": 0.2370917543905734
|
||||
},
|
||||
"link_10.0m": {
|
||||
"p=0.10": 0.3352983745859798,
|
||||
"p=0.25": 0.48396151706514845,
|
||||
"p=0.50": 0.5588306243099662,
|
||||
"p=0.75": 0.48396151706514845,
|
||||
"p=0.90": 0.3352983745859797
|
||||
}
|
||||
},
|
||||
"5.0": {
|
||||
"wavelength_mm": 59.96,
|
||||
"link_2.0m": {
|
||||
"p=0.10": 0.10388840166255327,
|
||||
"p=0.25": 0.14994999166388773,
|
||||
"p=0.50": 0.17314733610425545,
|
||||
"p=0.75": 0.14994999166388773,
|
||||
"p=0.90": 0.10388840166255325
|
||||
},
|
||||
"link_5.0m": {
|
||||
"p=0.10": 0.16426198586404586,
|
||||
"p=0.25": 0.23709175439057345,
|
||||
"p=0.50": 0.27376997644007645,
|
||||
"p=0.75": 0.23709175439057345,
|
||||
"p=0.90": 0.16426198586404583
|
||||
},
|
||||
"link_10.0m": {
|
||||
"p=0.10": 0.23230152819127128,
|
||||
"p=0.25": 0.3352983745859798,
|
||||
"p=0.50": 0.3871692136521188,
|
||||
"p=0.75": 0.3352983745859798,
|
||||
"p=0.90": 0.23230152819127126
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
#!/usr/bin/env python3
|
||||
"""R6 — Fresnel-zone forward model for CSI sensitivity.
|
||||
|
||||
See docs/research/sota-2026-05-22/R6-fresnel-forward-model.md.
|
||||
|
||||
For a Tx-Rx link, the first Fresnel zone is a prolate ellipsoid whose
|
||||
radius at fractional position p (0..1) along the LOS path is:
|
||||
|
||||
r_n(p) = sqrt(n * lambda * d * p * (1-p)) (for n=1)
|
||||
|
||||
A point scatterer that crosses the first Fresnel zone perpendicular to
|
||||
the LOS introduces a path-length delta:
|
||||
|
||||
delta_l(x) = sqrt(d1^2 + x^2) + sqrt(d2^2 + x^2) - d1 - d2
|
||||
|
||||
where x is the perpendicular offset. Phase shift on subcarrier k:
|
||||
|
||||
phi_k = 2 * pi * f_k * delta_l / c
|
||||
|
||||
This is the bedrock forward model that the existing `wifi-densepose-signal`
|
||||
DSP implicitly assumes. We make it explicit so:
|
||||
|
||||
1. R12's revision path (PABS basis grounded in Fresnel geometry) has
|
||||
somewhere to start.
|
||||
2. R10's foliage-range estimates can be sanity-checked against Fresnel-
|
||||
ellipsoid clearance, not just FSPL + foliage attenuation.
|
||||
3. Multi-subcarrier interference patterns from real scatterers become
|
||||
predictable rather than mysterious.
|
||||
|
||||
Pure NumPy — emits a JSON file with the predictions.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
from pathlib import Path
|
||||
import numpy as np
|
||||
|
||||
C = 2.998e8 # speed of light, m/s
|
||||
|
||||
|
||||
def wavelength_m(freq_ghz: float) -> float:
|
||||
return C / (freq_ghz * 1e9)
|
||||
|
||||
|
||||
def fresnel_radius_m(freq_ghz: float, link_length_m: float, p: float, n: int = 1) -> float:
|
||||
"""Radius of the n-th Fresnel zone at fractional link position p.
|
||||
|
||||
p=0 is at Tx, p=1 is at Rx. r is maximum at p=0.5 (midpoint).
|
||||
"""
|
||||
lam = wavelength_m(freq_ghz)
|
||||
return float(np.sqrt(n * lam * link_length_m * p * (1.0 - p)))
|
||||
|
||||
|
||||
def path_delta_m(d1: float, d2: float, perpendicular_offset_m: float) -> float:
|
||||
"""Extra path length introduced by a point scatterer at perpendicular
|
||||
offset x from the LOS, with d1 / d2 the Tx- and Rx-side LOS distances."""
|
||||
x = perpendicular_offset_m
|
||||
return float(np.sqrt(d1**2 + x**2) + np.sqrt(d2**2 + x**2) - (d1 + d2))
|
||||
|
||||
|
||||
def csi_phase_shift_rad(freq_ghz: float, path_delta: float) -> float:
|
||||
"""Phase shift on a single subcarrier given the path-length delta."""
|
||||
return 2 * np.pi * freq_ghz * 1e9 * path_delta / C
|
||||
|
||||
|
||||
def fresnel_zone_classification(freq_ghz: float, link_length_m: float,
|
||||
scatterer_offset_m: float,
|
||||
scatterer_position_m: float) -> str:
|
||||
"""Is the scatterer inside the n-th Fresnel zone?
|
||||
|
||||
Zone n is the volume where r_{n-1} < |offset| <= r_n.
|
||||
"""
|
||||
p = scatterer_position_m / link_length_m
|
||||
if not (0 <= p <= 1):
|
||||
return "outside-link"
|
||||
abs_off = abs(scatterer_offset_m)
|
||||
for n in range(1, 10):
|
||||
r = fresnel_radius_m(freq_ghz, link_length_m, p, n)
|
||||
if abs_off <= r:
|
||||
return f"zone-{n}"
|
||||
return "far-field"
|
||||
|
||||
|
||||
def subcarrier_phase_sweep(freq_ghz: float, link_length_m: float,
|
||||
scatterer_offset_m: float,
|
||||
scatterer_position_m: float,
|
||||
n_subcarriers: int = 52,
|
||||
subcarrier_spacing_khz: float = 312.5) -> dict:
|
||||
"""Predict per-subcarrier phase shift from a single scatterer.
|
||||
|
||||
Uses 802.11n/ac 20 MHz channels: 52 used subcarriers, spaced 312.5 kHz.
|
||||
Subcarrier indices -26..26 excluding DC/pilot tones (we don't bother
|
||||
excluding here — pure sweep).
|
||||
"""
|
||||
d1 = scatterer_position_m
|
||||
d2 = link_length_m - scatterer_position_m
|
||||
if d1 <= 0 or d2 <= 0:
|
||||
raise ValueError("scatterer_position_m must be strictly inside [0, link_length_m]")
|
||||
delta = path_delta_m(d1, d2, scatterer_offset_m)
|
||||
# subcarrier frequencies
|
||||
sub_offsets_hz = (np.arange(n_subcarriers) - n_subcarriers // 2) * subcarrier_spacing_khz * 1e3
|
||||
f_per_sub = freq_ghz * 1e9 + sub_offsets_hz
|
||||
phases_rad = 2 * np.pi * f_per_sub * delta / C
|
||||
return {
|
||||
"path_delta_m": delta,
|
||||
"phase_rad_per_subcarrier": phases_rad.tolist(),
|
||||
"phase_rad_min": float(phases_rad.min()),
|
||||
"phase_rad_max": float(phases_rad.max()),
|
||||
"phase_rad_spread": float(phases_rad.max() - phases_rad.min()),
|
||||
"phase_wraps": int(np.floor((phases_rad.max() - phases_rad.min()) / (2 * np.pi))),
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--out", default="examples/research-sota/r6_fresnel_results.json")
|
||||
args = parser.parse_args()
|
||||
|
||||
# Scenario: 5-metre indoor link (typical bedroom/lab setup)
|
||||
link_lengths = [2.0, 5.0, 10.0]
|
||||
freqs = [2.4, 5.0]
|
||||
p_grid = [0.1, 0.25, 0.5, 0.75, 0.9] # link position fractions
|
||||
|
||||
out = {
|
||||
"model": "first-Fresnel-zone ellipsoid + per-subcarrier path-delta forward model",
|
||||
"constants": {"c_mps": C},
|
||||
"scenarios": [],
|
||||
}
|
||||
|
||||
# 1. First Fresnel radii (the basic envelope)
|
||||
fresnel = {}
|
||||
for f in freqs:
|
||||
fresnel[str(f)] = {}
|
||||
lam = wavelength_m(f)
|
||||
fresnel[str(f)]["wavelength_mm"] = lam * 1000
|
||||
for L in link_lengths:
|
||||
radii = {f"p={p:.2f}": fresnel_radius_m(f, L, p, n=1) for p in p_grid}
|
||||
fresnel[str(f)][f"link_{L}m"] = radii
|
||||
out["first_fresnel_radii_m"] = fresnel
|
||||
|
||||
# 2. Single-scatterer per-subcarrier sweep
|
||||
# Scatterer at midpoint, 10 cm off LOS (human standing near link)
|
||||
scenarios = [
|
||||
("human-standing-at-midpoint", 5.0, 0.10, 2.5),
|
||||
("human-walking-into-fresnel", 5.0, 0.25, 2.5),
|
||||
("scatterer-outside-fresnel", 5.0, 1.50, 2.5),
|
||||
("scatterer-near-Tx", 5.0, 0.05, 0.5),
|
||||
]
|
||||
for name, L, x_off, x_pos in scenarios:
|
||||
case = {"name": name, "link_m": L, "scatterer_offset_m": x_off,
|
||||
"scatterer_position_m": x_pos}
|
||||
for f in freqs:
|
||||
r1 = fresnel_radius_m(f, L, x_pos / L, n=1)
|
||||
zone = fresnel_zone_classification(f, L, x_off, x_pos)
|
||||
sweep = subcarrier_phase_sweep(f, L, x_off, x_pos)
|
||||
case[f"freq_{f}_GHz"] = {
|
||||
"first_fresnel_radius_m": r1,
|
||||
"zone": zone,
|
||||
**sweep,
|
||||
}
|
||||
out["scenarios"].append(case)
|
||||
|
||||
Path(args.out).parent.mkdir(parents=True, exist_ok=True)
|
||||
Path(args.out).write_text(json.dumps(out, indent=2))
|
||||
|
||||
print("=== First Fresnel zone radii (m) ===")
|
||||
print(f"{'freq':>5} {'lambda':>8} {'link':>5} " + " ".join(f"p={p:.2f}" for p in p_grid))
|
||||
for f in freqs:
|
||||
lam_mm = wavelength_m(f) * 1000
|
||||
for L in link_lengths:
|
||||
radii = [fresnel_radius_m(f, L, p, n=1) for p in p_grid]
|
||||
row = f"{f:>5.1f} {lam_mm:>5.1f}mm {L:>4.1f}m " + " ".join(f"{r:>6.3f}" for r in radii)
|
||||
print(row)
|
||||
print()
|
||||
|
||||
print("=== Single-scatterer per-subcarrier predictions ===")
|
||||
for case in out["scenarios"]:
|
||||
print(f"{case['name']:>32} ", end="")
|
||||
for f in freqs:
|
||||
k = f"freq_{f}_GHz"
|
||||
v = case[k]
|
||||
print(f"{f:.1f}GHz: r1={v['first_fresnel_radius_m']*100:.1f}cm "
|
||||
f"zone={v['zone']:<8} "
|
||||
f"phase-spread={np.degrees(v['phase_rad_spread']):.3f} deg "
|
||||
f"wraps={v['phase_wraps']}", end=" ")
|
||||
print()
|
||||
print()
|
||||
print(f"Wrote {args.out}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user