11from __future__ import annotations
22
33import json
4+ import re
5+
6+ import pandas as pd
7+ import xarray as xa
48
59from openlifu .util .types import PathLike
610from openlifu .xdc .transducer import Transducer
711from openlifu .xdc .transducerarray import TransducerArray
812
13+ FOCAL_GAIN_LUT = xa .DataArray .from_dict (
14+ {'dims' : ('f0' , 'crosstalk' ),
15+ 'attrs' : {},
16+ 'data' : [[2.807589054107666 ,
17+ 3.2286391258239746 ,
18+ 3.649686813354492 ,
19+ 3.8181092739105225 ,
20+ 4.07073974609375 ,
21+ 4.491786956787109 ,
22+ 4.912837505340576 ,
23+ 5.333885669708252 ,
24+ 5.754938125610352 ,
25+ 6.175983428955078 ,
26+ 6.597033500671387 ,
27+ 7.01808500289917 ],
28+ [2.90433931350708 ,
29+ 3.3324313163757324 ,
30+ 3.760524272918701 ,
31+ 3.931760549545288 ,
32+ 4.188616752624512 ,
33+ 4.616710662841797 ,
34+ 5.044803142547607 ,
35+ 5.472893714904785 ,
36+ 5.900986671447754 ,
37+ 6.3290791511535645 ,
38+ 6.757172107696533 ,
39+ 7.185482501983643 ],
40+ [2.9909276962280273 ,
41+ 3.428293466567993 ,
42+ 3.865659713745117 ,
43+ 4.040605068206787 ,
44+ 4.3030242919921875 ,
45+ 4.740390777587891 ,
46+ 5.1777544021606445 ,
47+ 5.615119934082031 ,
48+ 6.052487373352051 ,
49+ 6.489851951599121 ,
50+ 6.927217483520508 ,
51+ 7.364583969116211 ],
52+ [3.0771772861480713 ,
53+ 3.5201354026794434 ,
54+ 3.9643561840057373 ,
55+ 4.142045497894287 ,
56+ 4.408576965332031 ,
57+ 4.852799415588379 ,
58+ 5.297021865844727 ,
59+ 5.741242408752441 ,
60+ 6.185462474822998 ,
61+ 6.629685878753662 ,
62+ 7.073910713195801 ,
63+ 7.518129348754883 ],
64+ [3.170368194580078 ,
65+ 3.617199182510376 ,
66+ 4.064029693603516 ,
67+ 4.242762565612793 ,
68+ 4.51104211807251 ,
69+ 4.961826801300049 ,
70+ 5.4126152992248535 ,
71+ 5.863399505615234 ,
72+ 6.314184665679932 ,
73+ 6.764969825744629 ,
74+ 7.215755462646484 ,
75+ 7.666543960571289 ],
76+ [3.242729663848877 ,
77+ 3.6979565620422363 ,
78+ 4.153182506561279 ,
79+ 4.335274696350098 ,
80+ 4.6084089279174805 ,
81+ 5.064931869506836 ,
82+ 5.521984100341797 ,
83+ 5.979033946990967 ,
84+ 6.4360833168029785 ,
85+ 6.89313268661499 ,
86+ 7.350184440612793 ,
87+ 7.8072357177734375 ],
88+ [3.327850103378296 ,
89+ 3.783803701400757 ,
90+ 4.245124340057373 ,
91+ 4.429731845855713 ,
92+ 4.706640720367432 ,
93+ 5.168155193328857 ,
94+ 5.6296706199646 ,
95+ 6.091186046600342 ,
96+ 6.552703380584717 ,
97+ 7.014214992523193 ,
98+ 7.475728988647461 ,
99+ 7.937244415283203 ],
100+ [3.415055990219116 ,
101+ 3.8783867359161377 ,
102+ 4.341715335845947 ,
103+ 4.527048110961914 ,
104+ 4.8050456047058105 ,
105+ 5.268375396728516 ,
106+ 5.731706142425537 ,
107+ 6.195037841796875 ,
108+ 6.658364295959473 ,
109+ 7.12183952331543 ,
110+ 7.587955474853516 ,
111+ 8.054073333740234 ],
112+ [5.705652713775635 ,
113+ 5.966911792755127 ,
114+ 6.2332234382629395 ,
115+ 6.343565940856934 ,
116+ 6.509076118469238 ,
117+ 6.784928798675537 ,
118+ 7.060781478881836 ,
119+ 7.336633682250977 ,
120+ 7.612486362457275 ,
121+ 7.888339519500732 ,
122+ 8.164192199707031 ,
123+ 8.447998046875 ],
124+ [5.73893404006958 ,
125+ 5.998416423797607 ,
126+ 6.260423183441162 ,
127+ 6.365228652954102 ,
128+ 6.522432327270508 ,
129+ 6.784440994262695 ,
130+ 7.046449184417725 ,
131+ 7.310236930847168 ,
132+ 7.583878517150879 ,
133+ 7.8575215339660645 ,
134+ 8.131163597106934 ,
135+ 8.404805183410645 ],
136+ [5.780664920806885 ,
137+ 6.028132915496826 ,
138+ 6.275601387023926 ,
139+ 6.3745880126953125 ,
140+ 6.523068904876709 ,
141+ 6.777853965759277 ,
142+ 7.03900146484375 ,
143+ 7.300150394439697 ,
144+ 7.561298370361328 ,
145+ 7.822445869445801 ,
146+ 8.083595275878906 ,
147+ 8.350235939025879 ],
148+ [5.814091205596924 ,
149+ 6.0464091300964355 ,
150+ 6.284290313720703 ,
151+ 6.383488178253174 ,
152+ 6.532283306121826 ,
153+ 6.780277252197266 ,
154+ 7.028269290924072 ,
155+ 7.27626371383667 ,
156+ 7.524255752563477 ,
157+ 7.772250175476074 ,
158+ 8.040055274963379 ,
159+ 8.318523406982422 ],
160+ [5.836524486541748 ,
161+ 6.067535400390625 ,
162+ 6.301788806915283 ,
163+ 6.3954901695251465 ,
164+ 6.536042213439941 ,
165+ 6.770293712615967 ,
166+ 7.00454568862915 ,
167+ 7.238796710968018 ,
168+ 7.482921600341797 ,
169+ 7.740076541900635 ,
170+ 8.005291938781738 ,
171+ 8.270505905151367 ],
172+ [5.86801815032959 ,
173+ 6.0880255699157715 ,
174+ 6.308032989501953 ,
175+ 6.396038055419922 ,
176+ 6.528041839599609 ,
177+ 6.749823093414307 ,
178+ 6.9840264320373535 ,
179+ 7.218224048614502 ,
180+ 7.4528584480285645 ,
181+ 7.70409631729126 ,
182+ 7.955334663391113 ,
183+ 8.206572532653809 ],
184+ [5.892360210418701 ,
185+ 6.097702980041504 ,
186+ 6.303044319152832 ,
187+ 6.390904903411865 ,
188+ 6.523708343505859 ,
189+ 6.7450480461120605 ,
190+ 6.966385841369629 ,
191+ 7.187726020812988 ,
192+ 7.417387962341309 ,
193+ 7.661881923675537 ,
194+ 7.91318941116333 ,
195+ 8.164498329162598 ],
196+ [5.90617036819458 ,
197+ 6.104805946350098 ,
198+ 6.312875747680664 ,
199+ 6.396102428436279 ,
200+ 6.520944595336914 ,
201+ 6.729012966156006 ,
202+ 6.937082290649414 ,
203+ 7.15734338760376 ,
204+ 7.39542818069458 ,
205+ 7.6335129737854 ,
206+ 7.8715996742248535 ,
207+ 8.1096830368042 ]],
208+ 'coords' : {'f0' : {'dims' : ('f0' ,),
209+ 'attrs' : {},
210+ 'data' : [130000.0 ,
211+ 135000.0 ,
212+ 140000.0 ,
213+ 145000.0 ,
214+ 150000.0 ,
215+ 155000.0 ,
216+ 160000.0 ,
217+ 165000.0 ,
218+ 375000.0 ,
219+ 380000.0 ,
220+ 385000.0 ,
221+ 390000.0 ,
222+ 395000.0 ,
223+ 400000.0 ,
224+ 405000.0 ,
225+ 410000.0 ]},
226+ 'crosstalk' : {'dims' : ('crosstalk' ,),
227+ 'attrs' : {},
228+ 'data' : [0.0 ,
229+ 0.05 ,
230+ 0.1 ,
231+ 0.12 ,
232+ 0.15000000000000002 ,
233+ 0.2 ,
234+ 0.25 ,
235+ 0.30000000000000004 ,
236+ 0.35000000000000003 ,
237+ 0.4 ,
238+ 0.45 ,
239+ 0.5 ]}},
240+ 'name' : 'focal_gain' })
9241
10242def load_transducer_from_file (transducer_filepath : PathLike , convert_array :bool = True ) -> Transducer | TransducerArray :
11243 """Load a Transducer or TransducerArray from file, depending on the "type" field in the file.
@@ -28,3 +260,45 @@ def load_transducer_from_file(transducer_filepath : PathLike, convert_array:bool
28260 else :
29261 transducer = Transducer .from_file (transducer_filepath )
30262 return transducer
263+
264+ def read_test_report (filename : PathLike ) -> pd .DataFrame :
265+ sections = [{"name" : "info" , "start_row" : 3 , "nrows" : 3 },
266+ {"name" : "txm" , "start_row" : 9 , "nrows" : 5 },
267+ {"name" : "console" , "start_row" : 17 , "nrows" : 3 },
268+ {"name" : "scans" , "start_row" : 23 , "nrows" : 7 },
269+ {"name" : "freq" , "start_row" : 33 , "nrows" : 9 },
270+ {"name" : "voltage" , "start_row" : 45 , "nrows" : 7 }]
271+
272+ all_data = []
273+ for section in sections :
274+ report_df = pd .read_excel (filename , sheet_name = "Report" , skiprows = section ["start_row" ], nrows = section ["nrows" ], index_col = 0 , usecols = "A:C" )
275+ report_df ["Section" ] = section ["name" ]
276+ all_data .append (report_df )
277+
278+ report_df = pd .concat (all_data )
279+ return report_df
280+
281+ def report_to_matrix_dict (report_df : pd .DataFrame , focal_gain_lut = FOCAL_GAIN_LUT ) -> dict :
282+ ROW_SN = 'B.1'
283+ ROW_FREQ = 'B.2'
284+ ROW_VOLTAGE = 'E.1'
285+ LIFU_400 = {'id' : r'txm_400_{sn}' , 'name' : r'TXM 400kHz (S/N {sn})' , 'nx' : 8 , 'ny' : 8 , 'pitch' : 5 , 'frequency' : 400e3 , 'kerf' : 0.3 , 'crosstalk_frac' : 0.12 , 'crosstalk_dist' : 5.05e-3 }
286+ LIFU_155 = {'id' : r'txm_155_{sn}' , 'name' : r'TXM 155kHz (S/N {sn})' , 'nx' : 8 , 'ny' : 8 , 'pitch' : 5 , 'frequency' : 155e3 , 'kerf' : 0.3 , 'crosstalk_frac' : 0.12 , 'crosstalk_dist' : 5.05e-3 }
287+ LIFU_MODULES = {400 : LIFU_400 , 155 : LIFU_155 }
288+ freq_kHz = report_df .loc [ROW_FREQ ]["Value" ]
289+ voltage = report_df .loc [ROW_VOLTAGE ]["Value" ]
290+ sn = report_df .loc [ROW_SN ]["Value" ]
291+ pattern = r'[^a-zA-Z0-9\-\_]'
292+ replacement = ''
293+ sn = re .sub (pattern , replacement , sn )
294+ matrix_dict = LIFU_MODULES [freq_kHz ]
295+ freq_df = report_df [report_df ["Section" ] == "freq" ].copy ().drop (columns = ["Section" ])
296+ freq_df = freq_df .rename (columns = {"Value" : "PNP" })
297+ freq_df = freq_df [freq_df ['Item' ].str .startswith ("PNP" )]
298+ freq_df ["Frequency" ] = freq_df ['Item' ].apply (lambda x : float (re .search (r"(?<=^PNP \()\d+(?= kHz\)$)" , x ).group (0 )))
299+ freq_df ['focal_gain' ] = freq_df ['Frequency' ].apply (lambda f : focal_gain_lut .interp (f0 = f * 1e3 , crosstalk = matrix_dict ['crosstalk_frac' ]).item ())
300+ freq_df ['Sensitivity' ] = freq_df ['PNP' ].astype (float )* 1e6 / freq_df ['focal_gain' ]/ voltage
301+ matrix_dict ['sensitivity' ] = {f * 1e3 :sens for f , sens , in zip (freq_df ["Frequency" ], freq_df ['Sensitivity' ])}
302+ matrix_dict ['id' ] = matrix_dict ['id' ].format (sn = sn .lower ())
303+ matrix_dict ['name' ] = matrix_dict ['name' ].format (sn = sn )
304+ return matrix_dict
0 commit comments