3
3
import serial .tools .list_ports
4
4
import subprocess
5
5
import time
6
+ import json
7
+ import math
8
+ import cmath
6
9
#import VNA
7
10
#from VNA_Example_LibreVNA import VNA
8
11
from VNA_Example_SNA5000A import VNA
@@ -19,6 +22,7 @@ def SCPICommand(ser, cmd: str) -> str:
19
22
20
23
parser = argparse .ArgumentParser (description = "Helps with creation of factory calibration coefficients for a new LibreCAL device" )
21
24
parser .add_argument ('-f' , '--flash' , help = 'Flash the firmware file first' , metavar = "firmware" )
25
+ parser .add_argument ('-l' , '--limits' , help = 'Enables limit checking on coefficients with limits from json file' , metavar = "json_limit_file" )
22
26
23
27
args = parser .parse_args ()
24
28
@@ -34,7 +38,6 @@ def SCPICommand(ser, cmd: str) -> str:
34
38
print ("Firmware flashed, waiting for device to boot" )
35
39
time .sleep (2 )
36
40
37
-
38
41
# Try to find the connected LibreCAL
39
42
port = None
40
43
for p in serial .tools .list_ports .comports ():
@@ -47,7 +50,7 @@ def SCPICommand(ser, cmd: str) -> str:
47
50
48
51
print ("Found LibreCAL device on port " + port .device )
49
52
50
- ser = serial .Serial (port .device , timeout = 1 )
53
+ ser = serial .Serial (port .device , timeout = 2 )
51
54
idn = SCPICommand (ser , "*IDN?" ).split ("," )
52
55
if idn [0 ] != "LibreCAL" :
53
56
raise Exception ("Invalid *IDN response: " + idn )
@@ -214,6 +217,94 @@ def takeMeasurements(portmapping : dict):
214
217
215
218
print ("\r \n Measurements complete." )
216
219
220
+ if args .limits :
221
+ jlimits = None
222
+ try :
223
+ f = open (args .limits )
224
+ jlimits = json .load (f )
225
+ except Exception as e :
226
+ raise Exception ("Failed to parse limit file" )
227
+
228
+
229
+ for i in jlimits :
230
+ # grab the specified measurement
231
+ measurements = {}
232
+ if i == "OPEN" :
233
+ measurements = Opens
234
+ elif i == "SHORT" :
235
+ measurements = Shorts
236
+ elif i == "LOAD" :
237
+ measurements = Loads
238
+ elif i == "THRU REFLECTION" :
239
+ for through in Throughs :
240
+ measurements [through + "_S11" ] = Throughs [through ]["S11" ]
241
+ measurements [through + "_S22" ] = Throughs [through ]["S22" ]
242
+ elif i == "THRU TRANSMISSION" :
243
+ for through in Throughs :
244
+ measurements [through + "_S12" ] = Throughs [through ]["S12" ]
245
+ measurements [through + "_S21" ] = Throughs [through ]["S21" ]
246
+ elif i == "OPEN SHORT PHASE" :
247
+ for key in Opens .keys ():
248
+ if key not in Shorts :
249
+ # should not happen
250
+ raise RuntimeError ("Got an open measurement without corresponding short measurement at port " + str (key ))
251
+ samples = max (len (Opens [key ]), len (Shorts [key ]))
252
+ open_vs_short = []
253
+ for j in range (samples ):
254
+ if Opens [key ][j ][0 ] == Shorts [key ][j ][0 ]:
255
+ # this sample uses the same frequency (should always be the case)
256
+ open_vs_short .append ((Opens [key ][j ][0 ], Opens [key ][j ][1 ] / Shorts [key ][j ][1 ]))
257
+ else :
258
+ raise RuntimeError ("Open and short measurements have difference frequencies at port " + str (key ))
259
+ measurements [key ] = open_vs_short
260
+
261
+ if len (measurements ) == 0 :
262
+ # unknown limit keyword, nothing to check
263
+ continue
264
+
265
+ # iterate over and check the specified limits
266
+ for limit in jlimits [i ]:
267
+ # iterate over the measurements we need to check
268
+ for key in measurements .keys ():
269
+ # check every sample in the measurement
270
+ for sample in measurements [key ]:
271
+ if sample [0 ] < limit ["x1" ] or sample [0 ] > limit ["x2" ]:
272
+ # Sample not covered by this limit
273
+ continue
274
+ # calculate limit value for this sample
275
+ alpha = (sample [0 ] - limit ["x1" ]) / (limit ["x2" ] - limit ["x1" ])
276
+ limval = limit ["y1" ] + alpha * (limit ["y2" ] - limit ["y1" ])
277
+
278
+ # transform y value according to limit type
279
+ yval = None
280
+ if limit ["type" ] == "dB" :
281
+ yval = 20 * math .log10 (abs (sample [1 ]))
282
+ elif limit ["type" ] == "phase" :
283
+ yval = math .degrees (cmath .polar (sample [1 ])[1 ])
284
+ # contrain to [0, 360)
285
+ while yval < 0 :
286
+ yval += 360
287
+ while yval >= 360 :
288
+ yval -= 360
289
+ else :
290
+ # unknown limit type
291
+ raise Exception ("Unknown limit type: " + str (limit ["type" ]))
292
+
293
+ # perform the actual limit check
294
+ success = True
295
+ if limit ["limit" ] == "max" :
296
+ if yval > limval :
297
+ success = False
298
+ elif limit ["limit" ] == "min" :
299
+ if yval < limval :
300
+ success = False
301
+ else :
302
+ # unknown limit
303
+ raise Exception ("Unknown limit: " + str (limit ["limit" ]))
304
+ if not success :
305
+ # this limit failed
306
+ raise Exception ("Limit check failed for type " + str (i )+ " in measurement " + str (key )+ " at frequency " + str (sample [0 ])+ ": limit is " + str (limval )+ ", measured value is " + str (yval ))
307
+
217
308
rCoeffs = {}
218
309
tCoeffs = {}
219
310
0 commit comments