tsapython 2.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
tsapython/core.py ADDED
@@ -0,0 +1,2310 @@
1
+ #! /usr/bin/python3
2
+
3
+ ##------------------------------------------------------------------------------------------------\
4
+ # tinySA_python (tsapython)
5
+ # './src/tsapython/core.py'
6
+ # UNOFFICIAL Python API based on the tinySA official documentation at https://www.tinysa.org/wiki/
7
+ #
8
+ # references:
9
+ # https://tinysa.org/wiki/pmwiki.php?n=TinySA4.ConsoleCommands (NOTE: backwards compat not tested!)
10
+ # http://athome.kaashoek.com/tinySA/python/tinySA.py (existing library with some examples)
11
+ #
12
+ #
13
+ # This class was previously named tinySA_python. The rename is to cover the CORE device functionalities,
14
+ # with the device specifics being added as extra modules.
15
+ #
16
+ # Author(s): Lauren Linkous
17
+ # Last update: August 17, 2025
18
+ ##--------------------------------------------------------------------------------------------------\
19
+
20
+ import serial
21
+ import serial.tools.list_ports # COM search method wants full path
22
+ import numpy as np
23
+ import re
24
+
25
+
26
+ class tinySA():
27
+ def __init__(self, parent=None):
28
+ # serial port
29
+ self.ser = None
30
+
31
+ # message feedback
32
+ self.verboseEnabled = False
33
+ self.returnErrorByte = False
34
+
35
+
36
+ # VARS BELOW HERE will be largely replaced with device class config calls
37
+ # # this will allow for user settings and device presets
38
+
39
+ # other overrides
40
+ self.ultraEnabled = False
41
+ self.abortEnabled = False
42
+ self.harmonicEnabled = False
43
+
44
+ #select device vars - hardcoding for the Ultra for now
45
+ # device params
46
+ self.maxPoints = 450
47
+ # spectrum analyzer
48
+ self.minSADeviceFreq = 100e3 #100 kHz
49
+ self.maxSADeviceFreq = 15e9 #5.3 GHz for normal operation, but 12 GHz for edge of harmonics.
50
+ # signal generator
51
+ self.minSGDeviceFreq = 100e3 #100 kHz
52
+ self.maxSGDeviceFreq = 960e6 #960 MHz
53
+ # battery
54
+ self.maxDeviceBattery = 4095
55
+ # screen
56
+ self.screenWidth = 480
57
+ self.screenHeight = 320
58
+
59
+
60
+ ######################################################################
61
+ # Error and information printout
62
+ # set/get_verbose() - set how detailed the error printouts are
63
+ # print_message() - deal with the bool in one place
64
+ ######################################################################
65
+
66
+ def set_verbose(self, verbose=False):
67
+ self.verboseEnabled = verbose
68
+
69
+ def get_verbose(self):
70
+ return self.verboseEnabled
71
+
72
+ def print_message(self, msg):
73
+ if self.verboseEnabled == True:
74
+ print(msg)
75
+
76
+ ######################################################################
77
+ # Explicit error return
78
+ # set_error_byte_return() - set if explicit b'ERROR' is returned
79
+ # get_error_byte_return() - get the return mode True/False
80
+ # error_byte_return() - return 'ERROR' message or empty.
81
+ ######################################################################
82
+
83
+ def set_error_byte_return(self, errByte=False):
84
+ self.returnErrorByte = errByte
85
+
86
+ def get_error_byte_return(self):
87
+ return self.returnErrorByte
88
+
89
+ def error_byte_return(self):
90
+ if self.returnErrorByte == True:
91
+ return bytearray(b'ERROR')
92
+ else:
93
+ return bytearray(b'') # the default
94
+
95
+
96
+ ######################################################################
97
+ # Set Device Params
98
+ # Library specific functions. These set the boundaries & features for
99
+ # error checking in the library
100
+ #
101
+ # WARNING: these DO NOT change the settings on the DEVICE. just the library.
102
+ ######################################################################
103
+
104
+ def select_existing_device(self, tinySAModel):
105
+ # uses pre-set config files.
106
+ # tinySAModel var must be one of the following:
107
+ # "BASIC", "ZS405", "ZS406", "ZS407"
108
+ try:
109
+ noErrors = self.dev.select_preset_model(tinySAModel)
110
+ if noErrors == False:
111
+ print("ERROR: device configuration unable to be set.This feature is underdevelopment")
112
+ return
113
+
114
+ # set variables from device configs.
115
+ # these are placeholders tes for now
116
+ # device params
117
+ self.maxPoints = 450
118
+ # spectrum analyzer
119
+ self.minSADeviceFreq = 100e3 #100 kHz
120
+ self.maxSADeviceFreq = 12e9 #5.3 GHz for normal operation, but 12 GHz for edge of harmonics
121
+ # signal generator
122
+ self.minSGDeviceFreq = 100e3 #100 kHz
123
+ self.maxSGDeviceFreq = 960e6 #960 MHz
124
+ # battery
125
+ self.maxDeviceBattery = 4095
126
+ # screen
127
+ self.screenWidth = 480
128
+ self.screenHeight = 320
129
+
130
+
131
+ except:
132
+ print("ERROR: device configuration unable to be set.This feature is underdevelopment")
133
+
134
+ def load_custom_config(self, configFile):
135
+ # TODO: for loading modified or other devices working on the same firmware
136
+ pass
137
+
138
+
139
+
140
+ ######################################################################
141
+ # Direct overrides
142
+ # These are used during DEBUG or when device state/model is already known
143
+ # Not recommended unless you are sure of the device state
144
+ # and which settings each device has
145
+ # WARNING: these DO NOT change the settings on the DEVICE. just the library.
146
+ ######################################################################
147
+
148
+ # error check bools
149
+ def set_ultra_mode(self, ultraMode=False):
150
+ self.ultraEnabled = ultraMode
151
+
152
+ def set_abort_mode(self, abortMode=False):
153
+ self.abortEnabled = abortMode
154
+
155
+ def set_harmonic_mode(self, harmonicMode=False):
156
+ self.harmonicEnabled = harmonicMode
157
+
158
+ # error check boundaries
159
+ ## signal analyzer specific
160
+ def set_min_SA_freq(self, f):
161
+ self.minSADeviceFreq = float(f)
162
+
163
+ def get_min_SA_freq(self):
164
+ return self.minSADeviceFreq
165
+
166
+ def set_max_SA_freq(self, f):
167
+ self.maxSADeviceFreq = float(f)
168
+
169
+ def get_max_SA_freq(self):
170
+ return self.maxSADeviceFreq
171
+
172
+ ## signal generator specific
173
+ def set_min_SG_freq(self, f):
174
+ self.minSGDeviceFreq = float(f)
175
+
176
+ def get_min_SG_freq(self):
177
+ return self.minSGDeviceFreq
178
+
179
+ def set_max_SG_freq(self, f):
180
+ self.maxSGDeviceFreq = float(f)
181
+
182
+ def get_max_SG_freq(self):
183
+ return self.maxSGDeviceFreq
184
+
185
+
186
+
187
+ ######################################################################
188
+ # Serial management and message processing
189
+ ######################################################################
190
+
191
+ def autoconnect(self, timeout=1):
192
+ # attempt to autoconnect to a detected port.
193
+ # returns: found_bool, connected_bool
194
+ # True if successful, False otherwise
195
+
196
+ # List all available serial ports
197
+ ports = serial.tools.list_ports.comports()
198
+ # loop through the ports and print out info
199
+ for port_info in ports:
200
+
201
+ # print out which port we're trying
202
+ port = port_info.device
203
+ self.print_message(f"Checking port: {port}")
204
+ vid = port_info.vid
205
+ pid = port_info.pid
206
+
207
+ # check if it's a tinySA or nanoVNA:
208
+ if (vid==None):
209
+ pass
210
+ elif (hex(vid) == '0x483') and (hex(pid)=='0x5740'):
211
+ self.print_message(f"tinySA device identified at port: {port}")
212
+ connected_bool = self.connect(port, timeout)
213
+
214
+ return True, connected_bool
215
+
216
+
217
+ return False, False # no tinySA found, not connected
218
+
219
+
220
+ def connect(self, port, timeout=1):
221
+ # attempt connection to provided port.
222
+ # returns: True if successful, False otherwise
223
+
224
+ try:
225
+ self.ser = serial.Serial(port=port, timeout=timeout)
226
+ return True
227
+ except Exception as err:
228
+ self.print_message("ERROR: cannot open port at " + str(port))
229
+ self.print_message(err)
230
+ return False
231
+
232
+ def disconnect(self):
233
+ # closes the serial port
234
+ self.ser.close()
235
+
236
+
237
+ def tinySA_serial(self, writebyte, printBool=False, pts=None):
238
+ # write out to serial, get message back, clean up, return
239
+
240
+ # clear INPUT buffer
241
+ self.ser.reset_input_buffer()
242
+ # clear OUTPUT buffer
243
+ self.ser.reset_output_buffer()
244
+
245
+
246
+ self.ser.write(bytes(writebyte, 'utf-8'))
247
+ msgbytes = self.get_serial_return()
248
+ msgbytes = self.clean_return(msgbytes)
249
+
250
+ if printBool == True:
251
+ print(msgbytes) #overrides verbose for debug
252
+
253
+ return msgbytes
254
+
255
+
256
+ def get_serial_return(self):
257
+ # while there's a buffer, read in the returned message
258
+ # original buffer reading from: https://groups.io/g/tinysa/topic/tinysa_screen_capture_using/82218670
259
+
260
+ buffer = bytes()
261
+ while True:
262
+ if self.ser.in_waiting > 0:
263
+ buffer += self.ser.read(self.ser.in_waiting)
264
+ try:
265
+ # split the stream to take a chunk at a time
266
+ # get up to '>' of the prompt
267
+ complete = buffer[:buffer.index(b'>')+1]
268
+ # leave the rest in buffer
269
+ buffer = buffer[buffer.index(b'ch>')+1:]
270
+ except ValueError:
271
+ # this is an acceptable err, so can skip it and keep looping
272
+ continue
273
+ except Exception as err:
274
+ # otherwise, something else is wrong
275
+ self.print_message("ERROR: exception thrown while reading serial")
276
+ self.print_message(err)
277
+ return None
278
+ break
279
+
280
+ return bytearray(complete)
281
+
282
+
283
+ def read_until_end_marker(self, end_marker=b'}', timeout=10.0):
284
+ # scan and scan raw might return early with tinySA_serial
285
+ # so this is written to
286
+ import time
287
+
288
+ buffer = bytes()
289
+ start_time = time.time()
290
+
291
+ while True:
292
+ if self.ser.in_waiting > 0:
293
+ buffer += self.ser.read(self.ser.in_waiting)
294
+
295
+ # Check if we have the end marker
296
+ if end_marker in buffer:
297
+ # Find the position after the end marker
298
+ end_pos = buffer.find(end_marker) + len(end_marker)
299
+ complete = buffer[:end_pos]
300
+ # Keep any remaining data for next read
301
+ self.remaining_buffer = buffer[end_pos:]
302
+ return bytearray(complete)
303
+
304
+ # Timeout check
305
+ if time.time() - start_time > timeout:
306
+ self.print_message(f"WARNING: Timeout waiting for end marker {end_marker}")
307
+ break
308
+
309
+ time.sleep(0.01)
310
+
311
+ return bytearray(buffer)
312
+
313
+ def clean_return(self, data):
314
+ # takes in a bytearray and removes 1) the text up to the first '\r\n' (includes the command), an 2) the ending 'ch>'
315
+ # Find the first occurrence of \r\n (carriage return + newline)
316
+ first_newline_index = data.find(b'\r\n')
317
+ if first_newline_index != -1:
318
+ # Slice the bytearray to remove everything before and including the first '\r\n'
319
+ data = data[first_newline_index + 2:] # Skip past '\r\n'
320
+ # Check if the message ends with 'ch>'
321
+ if data.endswith(b'ch>'):
322
+ # Remove 'ch>' from the end
323
+ data = data[:-4] # Remove the last 4 bytes ('ch>')
324
+ return data
325
+
326
+ ######################################################################
327
+ # Reusable format checking functions
328
+ ######################################################################
329
+
330
+ def convert_frequency(self, txtstr):
331
+ # this takes the user input (as text) and converts it.
332
+ # From documentation:
333
+ # Frequencies can be specified using an integer optionally postfixed with a the letter
334
+ # 'k' for kilo 'M' for Mega or 'G' for Giga. E.g. 0.1M (100kHz), 500k (0.5MHz) or 12000000 (12MHz)
335
+ # However the abbreviation makes error checking with numerics more difficult. so convert everything to Hz.
336
+ # e notation is fine
337
+ pass
338
+
339
+ def convert_time(self, txtstr):
340
+ # this takes the user input (as text) and converts it.
341
+ # From documentation:
342
+ # Time is specified in seconds optionally postfixed with the letters 'm' for mili
343
+ # or 'u' for micro. E.g. 1 (1 second), 2.5 (2.5 seconds), 120m (120 milliseconds)
344
+
345
+
346
+
347
+ pass
348
+
349
+
350
+ def is_rgb24(self, hexStr):
351
+ # check if the string matches the pattern 0xRRGGBB
352
+ pattern = r"^0x[0-9A-Fa-f]{6}$"
353
+ return bool(re.match(pattern, hexStr))
354
+
355
+
356
+ ######################################################################
357
+ # Serial command config, input error checking
358
+ ######################################################################
359
+
360
+ def abort(self, val=None):
361
+ # Sets the abort enabled status (on/off)
362
+ # usage: abort [off|on]
363
+ # example return: bytearray(b'')
364
+
365
+ # #explicitly allowed vals
366
+ accepted_vals = ["off", "on"]
367
+
368
+ #check input
369
+ if (str(val) in accepted_vals): #toggle state
370
+ writebyte = 'abort '+str(val)+'\r\n'
371
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
372
+ if val == "on":
373
+ self.print_message("ABORT option ENABLED")
374
+ self.abortEnabled = True
375
+ elif val == "off":
376
+ self.print_message("ABORT option DISABLED")
377
+ self.abortEnabled = False
378
+ elif val == None: #action
379
+ if self.abortEnabled == True:
380
+ writebyte = 'abort\r\n'
381
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
382
+ else:
383
+ self.print_message("ABORT option must be ENABLED before use")
384
+ msgbytes = bytearray(b'')
385
+ else:
386
+ self.print_message("ERROR: abort() takes NONE|\"off\"|\"on\" as arguments")
387
+ msgbytes = bytearray(b'')
388
+ return msgbytes
389
+
390
+ def enable_abort(self):
391
+ # alias for abort()
392
+ return self.abort( "on")
393
+ def disable_abort(self):
394
+ # alias for abort()
395
+ return self.abort("off")
396
+ def abort_action(self):
397
+ # alias for abort()
398
+ return self.abort()
399
+
400
+ def actual_freq(self, val=None):
401
+ # Sets or gets the frequency correction set by CORRECT FREQUENCY menu in the expert menu settings
402
+ # related to freq_corr
403
+ # usage: actual_freq [{frequency}]
404
+ # example return: bytearray(b'3000000000\r')
405
+
406
+ if val == None:
407
+ #get the dac
408
+ writebyte = 'actual_freq\r\n'
409
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
410
+ elif (isinstance(val, (int, float))) and (self.minSADeviceFreq <= val <=self.maxSADeviceFreq ):
411
+ writebyte = 'actual_freq '+str(val)+'\r\n'
412
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
413
+ self.print_message("actual_freq set to " + str(val))
414
+ else:
415
+ self.print_message("ERROR: actual_freq() takes either None or integers")
416
+ msgbytes = self.error_byte_return()
417
+ return msgbytes
418
+
419
+ def set_actual_freq(self, val):
420
+ # alias for actual_freq()
421
+ return self.actual_freq(val)
422
+ def get_actual_freq(self):
423
+ # alias for actual_freq()
424
+ return self.actual_freq(None)
425
+
426
+
427
+ def agc(self, val='auto'):
428
+ # Enables/disables the build in Automatic Gain Control
429
+ # usage: agc 0..7|auto
430
+ # example return: bytearray(b'')
431
+
432
+ #explicitly allowed vals
433
+ accepted_vals = np.arange(0, 8, 1) # max exclusive
434
+ #check input
435
+ if (str(val) == "auto") or (val in accepted_vals):
436
+ writebyte = 'agc '+str(val)+'\r\n'
437
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
438
+ self.print_message("agc() set with " + str(val))
439
+ else:
440
+ self.print_message("ERROR: agc() takes vals [0 - 7]|\"auto\"")
441
+ msgbytes = self.error_byte_return()
442
+ return msgbytes
443
+
444
+ def set_agc(self, val):
445
+ # alias for agc()
446
+ return self.agc(val)
447
+
448
+ def attenuate(self, val='auto'):
449
+ # sets the internal attenuation to automatic or a specific value
450
+ # usage: attenuate [auto|0-31]
451
+ # example return: bytearray(b'')
452
+
453
+ #explicitly allowed vals
454
+ accepted_vals = np.arange(0, 31, 1) # max exclusive
455
+ #check input
456
+ if (str(val) == "auto") or (val in accepted_vals):
457
+ writebyte = 'attenuate '+str(val)+'\r\n'
458
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
459
+ self.print_message("attenuate() set with " + str(val))
460
+ else:
461
+ self.print_message("ERROR: attenuate() takes vals [0 - 31]|\"auto\"")
462
+ msgbytes = self.error_byte_return()
463
+ return msgbytes
464
+
465
+ def set_attenuation(self, val):
466
+ # alias for attenuate()
467
+ return self.attenuate(val)
468
+
469
+ def bulk(self):
470
+ # sent by tinySA when in auto refresh mode
471
+ # format: "bulk\r\n{X}{Y}{Width}{Height}
472
+ # {Pixeldata}\r\n"
473
+ # where all numbers are binary coded 2
474
+ # bytes little endian. The Pixeldata is
475
+ # encoded as 2 bytes per pixel. similar to fill()
476
+
477
+ writebyte = 'bulk\r\n'
478
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
479
+ self.print_message("bulk() called for screen data")
480
+ return msgbytes
481
+
482
+ def get_bulk_data(self):
483
+ # alias for bulk()
484
+ return self.bulk()
485
+
486
+ def calc(self, val="off"):
487
+ # sets or cancels one of the measurement modes
488
+ # the commands are the same as those listed
489
+ # in the MEASURE menu
490
+ # usage: calc off|minh|maxh|maxd|aver4|aver16|quasip
491
+ # example return:
492
+
493
+ #explicitly allowed vals
494
+ accepted_vals = ["off", "minh", "maxh", "maxd",
495
+ "aver4", "aver16", "quasip"]
496
+ #check input
497
+ if (str(val) in accepted_vals):
498
+ writebyte = 'calc '+str(val)+'\r\n'
499
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
500
+ self.print_message("calc() set with " + str(val))
501
+ else:
502
+ self.print_message("ERROR: calc() takes vals \"off\"|\"minh\"|\"maxh\"|\"maxd\"|\"aver4\"|\"aver16\"|\"quasip\"")
503
+ msgbytes = self.error_byte_return()
504
+ return msgbytes
505
+
506
+ def set_calc_off(self):
507
+ return self.calc("off")
508
+ def set_calc_minh(self):
509
+ return self.calc("minh")
510
+ def set_calc_maxh(self):
511
+ return self.calc("maxh")
512
+ def set_calc_maxd(self):
513
+ return self.calc("maxd")
514
+ def set_calc_aver4(self):
515
+ return self.calc("aver4")
516
+ def set_calc_aver16(self):
517
+ return self.calc("aver16")
518
+ def set_calc_quasip(self):
519
+ return self.calc("quasip")
520
+
521
+ def cal_output(self, val="off"):
522
+ # disables or sets the caloutput to a specified frequency in MHz
523
+ # usage: caloutput off|30|15|10|4|3|2|1
524
+ # example return: bytearray(b'')
525
+
526
+ #explicitly allowed vals
527
+ accepted_vals = ["off", 'off', 1,2,3,4,10,15,30]
528
+ #check input
529
+ if (val in accepted_vals):
530
+ writebyte = 'caloutput '+str(val)+'\r\n'
531
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
532
+ self.print_message("caloutput() set with " + str(val))
533
+ else:
534
+ self.print_message("ERROR: caloutput() takes vals 1|2|3|4|10|15|30|\"off\"")
535
+ msgbytes = self.error_byte_return()
536
+ return msgbytes
537
+
538
+ def set_cal_output_off(self):
539
+ # alias for cal_output()
540
+ return self.caloutput("off")
541
+ def set_cal_output_30(self):
542
+ # alias for cal_output()
543
+ return self.caloutput(30)
544
+ def set_cal_output_15(self):
545
+ # alias for cal_output()
546
+ return self.caloutput(15)
547
+ def set_cal_output_10(self):
548
+ # alias for cal_output()
549
+ return self.caloutput(10)
550
+ def set_cal_output_4(self):
551
+ # alias for cal_output()
552
+ return self.caloutput(4)
553
+ def set_cal_output_3(self):
554
+ # alias for cal_output()
555
+ return self.caloutput(3)
556
+ def set_cal_output_2(self):
557
+ # alias for cal_output()
558
+ return self.caloutput(2)
559
+ def set_cal_output_1(self):
560
+ # alias for cal_output()
561
+ return self.caloutput(1)
562
+
563
+
564
+ def capture(self):
565
+ # requests a screen dump to be sent in binary format
566
+ # of 320x240 pixels of each 2 bytes
567
+ # usage: capture
568
+ # example return: bytearray(b'\x00 ...\x00\x00\x00')
569
+ writebyte = 'capture\r\n'
570
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
571
+ self.print_message("capture() called for screen data")
572
+ return msgbytes
573
+
574
+ def capture_screen(self):
575
+ return self.capture()
576
+
577
+ def clear_config(self):
578
+ # resets the configuration data to factory defaults. requires password
579
+ # NOTE: does take other commands to fully clear all
580
+ # usage: clearconfig 1234
581
+ # example return: bytearray(b'Config and all cal data cleared.
582
+ # \r\nDo reset manually to take effect.
583
+ # Then do touch cal and save.\r')
584
+ writebyte = 'clearconfig 1234\r\n'
585
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
586
+ self.print_message("clear_config() with password. Config and all cal data cleared. \
587
+ Reset manually to take effect.")
588
+ return msgbytes
589
+
590
+ def clear_and_reset(self):
591
+ # alias function for full clear and reset process
592
+ self.clear_config()
593
+ self.reset()
594
+
595
+ def color(self, ID=None, RGB='0xF8FCF8'):
596
+ # sets or dumps the colors used
597
+ # usage: color [{id} {rgb24}]
598
+ # example return:
599
+
600
+ # explicitly allowed vals
601
+ accepted_ID = np.arange(0, 31, 1) # max exclusive
602
+
603
+ if ID == None:
604
+ # get the color
605
+ writebyte = 'color\r\n'
606
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
607
+ elif (ID in accepted_ID) and (self.is_rgb24(RGB)==True):
608
+ # set the color based on ID
609
+ writebyte = 'color ' + str(ID) + ' ' + str(RGB) + '\r\n'
610
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
611
+ self.print_message("color() set with ID: " +str(ID) + " RGB: " + str(RGB))
612
+ else:
613
+ self.print_message("ERROR: color() takes either None, or ID as int 0..31 and RGB as a hex value")
614
+ msgbytes = self.error_byte_return()
615
+ return msgbytes
616
+
617
+ def get_all_colors(self):
618
+ # alias for color(). returns array of all colors
619
+ return self.color()
620
+
621
+ def get_color(self, ID):
622
+ # alias for color(). val must be int 1-31
623
+ msgbytes = self.color()
624
+ # check if something has been returned, otherwise pass the error through
625
+ if len(msgbytes) > 10:
626
+ # Use regex to find the value at index ID
627
+ pattern = rf'\b{int(ID)}:\s*0x([0-9A-Fa-f]+)'
628
+ match = re.search(pattern, msgbytes)
629
+ if match:
630
+ return f"0x{match.group(1)}" #return rgb24 value if found
631
+
632
+ # if not found, then
633
+ self.print_message("ERROR: color() takes either None, or ID as int 0..31 and RGB as a hex value")
634
+ msgbytes = self.error_byte_return()
635
+ return msgbytes
636
+
637
+ def set_color(self, ID, val):
638
+ # alias for color()
639
+ return self.color(ID, val)
640
+
641
+
642
+ def command(self, val):
643
+ # if the command isn't already a function,
644
+ # use existing func setup to send command
645
+ writebyte = str(val) + '\r\n'
646
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
647
+ self.print_message("command() called with ::" + str(val))
648
+ return msgbytes
649
+
650
+
651
+ def correction(self, argName="low", slot=None, freq=None, val=None):
652
+ # sets or dumps the frequency level orrection table
653
+ # usage: correction [0..9 {frequency} {level dB}]
654
+ # usage: correction low|lna|ultra|ultra_lna|direct|direct_lna|harm|harm_lna|out|out_direct|out_adf|out_ultra|off|on 0-19 frequency(Hz) value(dB)
655
+ # example return:
656
+
657
+ # explicitly allowed vals
658
+ accepted_table_args = ["low", "lna", "ultra", "ultra_lna",
659
+ "direct", "direct_lna", "harm",
660
+ "harm_lna", "out", "out_direct",
661
+ "out_adf", "out_ultra", "off", "on"]
662
+
663
+ accepted_slots = np.arange(0, 20, 1) # max exclusive.
664
+
665
+ if (argName in accepted_table_args) and (slot==None):
666
+ # prints out the table as it currently is
667
+ writebyte = 'correction ' + str(argName)+ '\r\n'
668
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
669
+ else:
670
+ # check error conditions quickly since there's 4
671
+ if not(argName in accepted_table_args):
672
+ self.print_message("ERROR: correction() requires a table indicator. see documentation")
673
+ msgbytes = self.error_byte_return()
674
+ return msgbytes
675
+ if not(slot in accepted_slots):
676
+ self.print_message("ERROR: correction() requires a slot from ["+ str(accepted_slots) + "]. see documentation")
677
+ msgbytes = self.error_byte_return()
678
+ return msgbytes
679
+ if not(self.minSADeviceFreq<=freq) and not(freq<=self.maxSADeviceFreq):
680
+ self.print_message("ERROR: correction() frequency outside of device specs. see documentation")
681
+ msgbytes = self.error_byte_return()
682
+ return msgbytes
683
+ if not(-10<=val) and not(val<=35):
684
+ self.print_message("ERROR: correction() val dB outside of specs. see documentation")
685
+ msgbytes = self.error_byte_return()
686
+ return msgbytes
687
+ writebyte = 'correction ' + str(argName) + ' ' + str(slot) +\
688
+ ' ' + str(freq) + ' ' + str(val) + '\r\n'
689
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
690
+ self.print_message("correction() set with " + str(argName) + " " + str(slot) +\
691
+ " " + str(freq) + " " + str(val))
692
+ return msgbytes
693
+
694
+
695
+ #TODO ADD the CORRECTION setter shortcuts here.
696
+
697
+
698
+ def dac(self, val=None):
699
+ # sets or dumps the dac value
700
+ # usage: dac [0..4095]
701
+ # example return: bytearray(b'usage: dac {value(0-4095)}\r\ncurrent value: 1922\r')
702
+
703
+ if val == None:
704
+ #get the dac
705
+ writebyte = 'dac\r\n'
706
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
707
+ elif (isinstance(val, (int, float))) and (0<= val <=4095):
708
+ writebyte = 'dac '+str(val)+'\r\n'
709
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
710
+ self.print_message("dac set to " + str(val))
711
+ else:
712
+ self.print_message("ERROR: dac() takes either None or integers")
713
+ msgbytes = self.error_byte_return()
714
+ return msgbytes
715
+
716
+ def set_dac(self, val):
717
+ # alias for dac()
718
+ return self.dac(val)
719
+ def get_dac(self):
720
+ # alias for dac()
721
+ return self.dac()
722
+
723
+
724
+
725
+ def data(self, val=0):
726
+ # dumps the trace data.
727
+ # usage: data [0-2]
728
+ # 0=temp value, 1=stored trace, 2=measurement
729
+ # example return: bytearray(b'-8.671875e+01\r\n... -8.337500e+01\r\n-8.237500e+01\r')
730
+
731
+ #explicitly allowed vals
732
+ accepted_vals = [0,1,2]
733
+ #check input
734
+ if val in accepted_vals:
735
+ writebyte = 'data '+str(val)+'\r\n'
736
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
737
+ if val == 0:
738
+ self.print_message("returning temp value data")
739
+ elif val == 1:
740
+ self.print_message("returning stored trace data")
741
+ elif val == 2:
742
+ self.print_message("returning measurement data")
743
+ else:
744
+ self.print_message("ERROR: data() takes vals [0-2]")
745
+ msgbytes = self.error_byte_return()
746
+ return msgbytes
747
+
748
+ def get_temporary_data(self):
749
+ # alias func for data()
750
+ return self.data(val=0)
751
+ def get_stored_trace_data(self):
752
+ # alias func for data()
753
+ return self.data(val=1)
754
+ def dump_measurement_data(self):
755
+ # alias func for data()
756
+ return self.data(val=2)
757
+
758
+
759
+ def device_id(self, ID=None):
760
+ # sets or dumps a user settable number that can be used to identify a specific tinySA
761
+ # usage: deviceid [{number}]
762
+ # example return: bytearray(b'deviceid 12\r')
763
+
764
+ if ID == None:
765
+ #get the device ID
766
+ writebyte = 'deviceid\r\n'
767
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
768
+ elif isinstance(ID, int):
769
+ writebyte = 'deviceid '+str(ID)+'\r\n'
770
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
771
+ self.print_message("device ID set to " + str(ID))
772
+ else:
773
+ self.print_message("ERROR: device_id() takes either None or integers")
774
+ msgbytes = self.error_byte_return()
775
+ return msgbytes
776
+
777
+ def get_device_id(self):
778
+ # alias for device_id()
779
+ return self.device_id()
780
+
781
+ def set_device_id(self, ID):
782
+ # alias for device_id()
783
+ return self.device_id(ID)
784
+
785
+ def direct(self, val, freq):
786
+ # Output mode for generating a square wave signal between 830MHz and 1130MHz
787
+ # usage: direct {start|stop|on|off} {freq(Hz)}
788
+ # example return: ''
789
+
790
+ #explicitly allowed vals
791
+ accepted_vals = ["start", "stop",
792
+ "on", "off"]
793
+ #check input
794
+ if (str(val)=="on") or (str(val) =="off"):
795
+ writebyte = 'direct '+str(val)+'\r\n'
796
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
797
+ self.print_message("direct() set with " + str(val))
798
+ elif (str(val)=="start") or (str(val)=="stop"):
799
+ #TODO: add frequency checking here
800
+ writebyte = 'direct '+str(val)+' ' +str(freq)+ '\r\n'
801
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
802
+ self.print_message("direct() set with " + str(val) + "frequency of " + str(freq))
803
+ else:
804
+ self.print_message("ERROR: direct() takes val={'on', 'off', 'start', 'stop'}, freq=INT")
805
+ msgbytes = self.error_byte_return()
806
+ return msgbytes
807
+
808
+ def set_direct_on(self):
809
+ # alias for direct()
810
+ return self.direct("on")
811
+ def set_direct_off(self):
812
+ # alias for direct()
813
+ return self.direct("off")
814
+ def set_direct_start(self, freq):
815
+ # alias for direct()
816
+ return self.direct("start", freq)
817
+ def set_direct_stop(self, freq):
818
+ # alias for direct()
819
+ return self.direct("stop", freq)
820
+
821
+ def ext_gain(self, val):
822
+ # sets the external attenuation/amplification.
823
+ # Works in both input and output mode
824
+ # usage: ext_gain -100..100
825
+ # example return: ''
826
+
827
+ #check input
828
+ if (isinstance(val, (int, float))) and (-100<= val <=100):
829
+ writebyte = 'ext_gain '+str(val)+'\r\n'
830
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
831
+ self.print_message("ext_gain() set to " + str(val))
832
+ else:
833
+ self.print_message("ERROR: ext_gain() takes vals [-100 - 100]")
834
+ msgbytes = self.error_byte_return()
835
+ return msgbytes
836
+
837
+ def set_ext_gain(self, val):
838
+ # alias for ext_gain()
839
+ return self.ext_gain(val)
840
+
841
+
842
+ def fill(self):
843
+ # sent by tinySA when in auto refresh mode
844
+ # format: "fill\r\n{X}{Y}{Width}{Height}
845
+ # {Color}\r\n"
846
+ # where all numbers are binary coded 2
847
+ # bytes little endian. Similar ot bulk()
848
+
849
+ writebyte = 'fill\r\n'
850
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
851
+ self.print_message("fill() called for screen data")
852
+ return msgbytes
853
+
854
+ def get_fill_data(self):
855
+ # alias for fill()
856
+ return self.fill()
857
+
858
+ def freq(self, val):
859
+ # pauses the sweep and sets the measurement frequency.
860
+ # usage: freq {frequency}
861
+ # example return: bytearray(b'')
862
+
863
+ #check input
864
+ if (isinstance(val, (int, float))) and (self.minSADeviceFreq<= val <=self.maxSADeviceFreq):
865
+ writebyte = 'freq '+str(val)+'\r\n'
866
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
867
+ self.print_message("freq() set to " + str(val))
868
+ else:
869
+ self.print_message("ERROR: freq() takes integer vals [100 kHz - 5.3 GHz] as Hz for the tinySA Ultra")
870
+ msgbytes = self.error_byte_return()
871
+ return msgbytes
872
+
873
+ def set_freq(self, val):
874
+ # freq() alias
875
+ return self.freq(val)
876
+
877
+
878
+ def freq_corr(self):
879
+ # get frequency correction
880
+ # usage: freq_corr
881
+ # example return: bytearray(b'0 ppb\r')
882
+
883
+ writebyte = 'freq_corr\r\n'
884
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
885
+ self.print_message("getting frequency correction")
886
+ return msgbytes
887
+
888
+ def get_frequency_correction(self):
889
+ # alias for freq_corr()
890
+ return self.freq_corr()
891
+
892
+ def frequencies(self):
893
+ # gets the frequencies used by the last sweep
894
+ # usage: frequencies
895
+ # example return: bytearray(b'1500000000\r\n... \r\n3000000000\r')
896
+
897
+ writebyte = 'frequencies\r\n'
898
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
899
+ self.print_message("getting frequencies from the last sweep")
900
+ return msgbytes
901
+
902
+ def get_last_freqs(self):
903
+ # get frequencies of last sweep
904
+ return self.frequencies()
905
+
906
+
907
+ def hop(self, start, stop, inc, outmask=None):
908
+ # this is a measurement, maybe a sample measurement. format looks like hop freqval integer
909
+ # usage: hop {start(Hz)} {stop(Hz)} {step(Hz) | points} [outmask]
910
+ # outmask: 1 is frequency, 2 is level
911
+ # example return: ''
912
+
913
+ if (isinstance(start, (int, float))) and (isinstance(stop, (int, float))) and (isinstance(inc, (int, float))):
914
+ if (isinstance(outmask, int)) and (0<outmask<3):
915
+ writebyte = 'hop ' + str(start) + ' ' + str(stop) + ' ' + str(inc) + ' ' + str(outmask) + '\r\n'
916
+
917
+ elif outmask ==None:
918
+ writebyte = 'hop ' + str(start) + ' ' + str(stop) + ' ' + str(inc) + '\r\n'
919
+
920
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
921
+ self.print_message("sampling over frequency range")
922
+ return msgbytes
923
+
924
+ else:
925
+ self.print_message("hop() takes arguments start=Int, stop=Int, inc=Int, outmask=None|Int")
926
+
927
+ return None
928
+
929
+ def get_sample_pts(self, start, stop, pts):
930
+ # alias for hop()
931
+ return self.hop(start, stop, pts, outmask=1)
932
+
933
+
934
+ def set_IF(self, val=0):
935
+ # the IF call, but avoiding reserved keywords
936
+ # sets the IF to automatic or a specific value. 0 means automatic
937
+ # usage: if ( 0 | 433M..435M )
938
+ # example return: ''
939
+
940
+ #check input
941
+ if (val == 0) or (val=='auto'):
942
+ writebyte = 'if '+str(0)+'\r\n'
943
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
944
+ self.print_message("setIF() set to auto")
945
+ elif ((433e6) <=val <=(435e6)):
946
+ writebyte = 'if '+str(val)+'\r\n'
947
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
948
+ self.print_message("setIF() set to " + str(val))
949
+ else:
950
+ self.print_message("ERROR: if() takes vals ['auto'|0|433M...435M] in Hz as integers")
951
+ msgbytes = self.error_byte_return()
952
+ return msgbytes
953
+
954
+ def set_IF1(self, val):
955
+ # usage: if1 {975M..979M}\r\n977.555902MHz
956
+ # example return: ''
957
+
958
+ #check input
959
+ if (val == 0) or (val=='auto'):
960
+ writebyte = 'if1 '+str(0)+'\r\n'
961
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
962
+ self.print_message("setIF1() set to auto")
963
+ elif ((975e6) <=val <=(979e6)):
964
+ writebyte = 'if1 '+str(val)+'\r\n'
965
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
966
+ self.print_message("setIF() set to " + str(val))
967
+ else:
968
+ self.print_message("ERROR: if1() takes vals ['auto'|0|975M...979M] in Hz as integers")
969
+ msgbytes = self.error_byte_return()
970
+ return msgbytes
971
+
972
+ def info(self):
973
+ # displays various SW and HW information
974
+ # usage: info
975
+ # example return: bytearray(b'tinySA ...\r')
976
+
977
+ writebyte = 'info\r\n'
978
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
979
+ self.print_message("returning device info()")
980
+ return msgbytes
981
+
982
+ def get_info(self):
983
+ # alias for info()
984
+ return self.info()
985
+
986
+
987
+ def level(self, val):
988
+ # sets the output level. Not all values in the range are available
989
+ # usage: level -76..13
990
+ # example return: b''
991
+
992
+ # explicitly allowed vals
993
+ accepted_vals = np.arange(-76, 14, 1) # max exclusive
994
+ #check input
995
+ if val in accepted_vals:
996
+ writebyte = 'level '+str(val)+'\r\n'
997
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
998
+ self.print_message("level() set to " + str(val))
999
+ else:
1000
+ self.print_message("ERROR: level() takes vals [-76 to 13]")
1001
+ self.print_message("ERROR: value given: " + str(val))
1002
+ msgbytes = self.error_byte_return()
1003
+ return msgbytes
1004
+
1005
+ def set_level(self, val):
1006
+ # alias for level()
1007
+ return self.level(val)
1008
+
1009
+
1010
+ def level_change(self, val):
1011
+ # sets the output level delta for low output mode level sweep
1012
+ # usage: levelchange -70..+70
1013
+ # example return: ''
1014
+
1015
+ #explicitly allowed vals
1016
+ accepted_vals = np.arange(-70, 71, 1) # max exclusive
1017
+ #check input
1018
+ if (val in accepted_vals):
1019
+ writebyte = 'levelchange '+str(val)+'\r\n'
1020
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
1021
+ self.print_message("levelchange() set to " + str(val))
1022
+ else:
1023
+ self.print_message("ERROR: levelchange() takes vals [-70 - 70]")
1024
+ self.print_message("ERROR: value set to" + str(val))
1025
+ msgbytes = self.error_byte_return()
1026
+ return msgbytes
1027
+
1028
+ def set_level_change(self, val):
1029
+ # alias for level_change()
1030
+ return self.level_change(val)
1031
+
1032
+
1033
+ def level_offset(self, val, offset, isOutput=False):
1034
+ # sets or dumps the level calibration data.
1035
+ # For the output corrections first ensure correct output
1036
+ # levels at maximum output level.
1037
+ # For the low output set the output to -50dBm and
1038
+ # measure and correct the level with
1039
+ # "leveloffset switch error" where for all output
1040
+ # leveloffset commands measure the level with the
1041
+ # leveloffset to zero and calculate
1042
+ # error = measured level - specified level
1043
+
1044
+
1045
+ # usage: leveloffset [low|switch|receive_switch|out_switch|lna|
1046
+ # harmonic|shift|shift1|shift2|shift3|drive1|drive2|drive3|
1047
+ # direct|direct_lna|ultra|ultra_lna|harmonic_lna|adf]
1048
+ # {output} [-20..+20]
1049
+
1050
+
1051
+ #NOTE: there's probably some limitations on which of these take the 'output' command,
1052
+ # but that error checking isn't done here YET
1053
+
1054
+ #explicitly allowed vals
1055
+ accepted_vals = ["low","switch","receive_switch","out_switch","lna",
1056
+ "harmonic","shift","shift1","shift2","shift3",
1057
+ "drive1","drive2","drive3","direct","direct_lna",
1058
+ "ultra","ultra_lna","harmonic_lna","adf"]
1059
+ #check input
1060
+ if (val in accepted_vals):
1061
+ if (-20.0<=offset<=20.0):
1062
+ if isOutput == True:
1063
+ # success message
1064
+ writebyte = 'leveloffset '+str(val)+ ' output ' + str(float(offset)) +'\r\n'
1065
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
1066
+ self.print_message("leveloffset() set to " + str(val) + " output " + str(offset))
1067
+
1068
+
1069
+ elif isOutput == False:
1070
+ # success message
1071
+ writebyte = 'leveloffset '+str(val)+ ' ' + str(float(offset)) +'\r\n'
1072
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
1073
+ self.print_message("leveloffset() set to " + str(val) + " " + str(offset))
1074
+
1075
+ else:
1076
+ # just for the error check when bulking this function out
1077
+ self.print_message("ERROR: leveloffset() value isOutput is a Boolean")
1078
+ self.print_message("ERROR: value set to" + str(isOutput))
1079
+ msgbytes = self.error_byte_return()
1080
+ return msgbytes
1081
+
1082
+
1083
+ else:
1084
+ self.print_message("ERROR: leveloffset() takes offset vals as floats [-20.0 - 20.0]")
1085
+ self.print_message("ERROR: value set to" + str(offset))
1086
+ msgbytes = self.error_byte_return()
1087
+
1088
+
1089
+ else:
1090
+ self.print_message("ERROR: leveloffset() takes value arguments low|switch|receive_switch|out_switch|lna|" \
1091
+ "harmonic|shift|shift1|shift2|shift3|drive1|drive2|drive3|direct|" \
1092
+ "direct_lna|ultra|ultra_lna|harmonic_lna|adf, ans specificed output and level")
1093
+ self.print_message("ERROR: value set to" + str(val))
1094
+ msgbytes = self.error_byte_return()
1095
+ return msgbytes
1096
+
1097
+
1098
+
1099
+
1100
+
1101
+
1102
+ msgbytes = self.error_byte_return()
1103
+ self.print_message("Function does not exist yet. error checking needed")
1104
+ return None
1105
+
1106
+ def line(self, val):
1107
+ # Disables the horizontal line or sets it to a specific level.
1108
+ # usage: line off|{level}
1109
+ # example return: ''
1110
+ if (val=="off"):
1111
+ writebyte = 'line '+str(val)+'\r\n'
1112
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
1113
+ self.print_message("horizontal line turned off")
1114
+ elif (isinstance(val, (int, float))): # or (isinstance(val, float)):
1115
+ writebyte = 'line '+str(val)+'\r\n'
1116
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
1117
+ self.print_message("horizontal line turned off")
1118
+ else:
1119
+ self.print_message("ERROR: line takes arguments 'off' or level")
1120
+ msgbytes = self.error_byte_return()
1121
+ return msgbytes
1122
+
1123
+ def line_off(self):
1124
+ # alias for line
1125
+ return self.line("off")
1126
+
1127
+ def set_line(self, val):
1128
+ # alias for line
1129
+ return self.line(val)
1130
+
1131
+ def load(self, val=0):
1132
+ # loads a previously stored preset,where 0 is the startup preset
1133
+ # usage: load [0-4]
1134
+ # example return: ''
1135
+
1136
+ #explicitly allowed vals
1137
+ accepted_vals = [0,1,2,3,4]
1138
+ #check input
1139
+ if (val in accepted_vals):
1140
+ writebyte = 'load '+str(val)+'\r\n'
1141
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
1142
+ self.print_message("load() called for preset # " + str(val))
1143
+ else:
1144
+ self.print_message("ERROR: load() takes vals [0 - 4]")
1145
+ msgbytes = self.error_byte_return()
1146
+ return msgbytes
1147
+
1148
+ def lna(self, val):
1149
+ # toggle lna usage off/on
1150
+ # usage: lna off|on
1151
+ # example return: ''
1152
+
1153
+ #explicitly allowed vals
1154
+ accepted_vals = ["on", "off"]
1155
+ #check input
1156
+ if (str(val) in accepted_vals):
1157
+ writebyte = 'lna '+str(val)+'\r\n'
1158
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
1159
+ self.print_message("lna() set to " + str(val))
1160
+ else:
1161
+ self.print_message("ERROR: lna() takes vals [on|off]")
1162
+ msgbytes = self.error_byte_return()
1163
+ return msgbytes
1164
+
1165
+ def set_lna_on(self):
1166
+ #alias for lna1()
1167
+ return self.lna("on")
1168
+ def set_lna_off(self):
1169
+ #alias for lna1()
1170
+ return self.lna("off")
1171
+
1172
+
1173
+ def lna2(self, val="auto"):
1174
+ # Set the second LNA usage off/on.
1175
+ # The Ultra Plus devices have a 2nd LNA at a higher frequency range.
1176
+ # usage: lna2 0..7|auto
1177
+ # example return: ''
1178
+
1179
+ #explicitly allowed vals
1180
+ accepted_vals = [0,1,2,3,4,5,6,7]
1181
+ #check input
1182
+ if (val == "auto") or (val in accepted_vals):
1183
+ writebyte = 'lna2 '+str(val)+'\r\n'
1184
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
1185
+ self.print_message("lna2() set to " + str(val))
1186
+ else:
1187
+ self.print_message("ERROR: lna2() takes vals [0 - 7]|auto")
1188
+ msgbytes = self.error_byte_return()
1189
+ return msgbytes
1190
+
1191
+ def set_lna2(self, val):
1192
+ #alias for lna2()
1193
+ return self.lna2(val)
1194
+
1195
+
1196
+ def marker(self, ID, val):
1197
+ # sets or dumps marker info.
1198
+ # where id=1..4 index=0..num_points-1
1199
+ # Marker levels will use the selected unit.
1200
+ # Marker peak will:
1201
+ # 1) activate the marker (if not done already),
1202
+ # 2) position the marker on the strongest signal, and
1203
+ # 3) display the marker info.
1204
+ # The frequency must be within the selected sweep range
1205
+ # usage: marker {id} on|off|peak|{freq}|{index}
1206
+ # example return: ''
1207
+
1208
+ #explicitly allowed vals
1209
+ accepted_vals = ["on", "off", "peak"]
1210
+ #check input
1211
+ if ID == None:
1212
+ self.print_message("ERROR: marker() takes ID=Int|0..4")
1213
+ msgbytes = self.error_byte_return()
1214
+ return msgbytes
1215
+
1216
+ if (str(val) in accepted_vals):
1217
+ writebyte = 'marker ' + str(ID) + ' ' +str(val)+'\r\n'
1218
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
1219
+ self.print_message("marker set to " + str(val))
1220
+ elif (isinstance(val, (int, float))): # or (isinstance(val, float)):
1221
+ writebyte = 'marker ' + str(ID) + ' ' +str(val)+'\r\n'
1222
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
1223
+ self.print_message("marker set to " + str(val))
1224
+ else:
1225
+ self.print_message("ERROR: marker() takes ID=Int|0..4, and frequency or index in Int or Float")
1226
+ msgbytes = self.error_byte_return()
1227
+ return msgbytes
1228
+
1229
+ def marker_on(self, ID):
1230
+ # alias for marker()
1231
+ self.marker(ID, "on")
1232
+ def marker_off(self, ID):
1233
+ # alias for marker()
1234
+ self.marker(ID, "off")
1235
+ def marker_peak(self, ID):
1236
+ # alias for marker()
1237
+ self.marker(ID, "peak")
1238
+ def marker_freq(self, ID, val):
1239
+ # alias for marker()
1240
+ self.marker(ID, val)
1241
+ def marker_index(self, ID, val):
1242
+ # alias for marker()
1243
+ self.marker(ID, val)
1244
+
1245
+ def menu(self, val):
1246
+ # The menu command can be used to activate any menu item
1247
+ # usage: menu {#} [{#} [{#} [{#}]]]
1248
+ # example return: ''
1249
+
1250
+ writebyte = 'menu ' + str(val) + '\r\n'
1251
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
1252
+ self.print_message("clicking menu button")
1253
+ return msgbytes
1254
+
1255
+ def mode(self, val1="low", val2="input"):
1256
+ # sets the mode of the tinySA
1257
+ # usage: mode low|high input|output
1258
+ # example return: ''
1259
+
1260
+ #explicitly allowed vals
1261
+ accepted_val1 = ["low", "high"]
1262
+ accepted_val2= ["input", "output"]
1263
+ #check input
1264
+ if (val1 in accepted_val1) and (val2 in accepted_val2):
1265
+ writebyte = 'mode '+str(val1)+ + ' ' +str(val2)+'\r\n'
1266
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
1267
+ else:
1268
+ self.print_message("ERROR: output() takes vals [on|off]")
1269
+ msgbytes = self.error_byte_return()
1270
+ return msgbytes
1271
+
1272
+ def set_low_input_mode(self):
1273
+ # alias for mode()
1274
+ return self.mode("low", "input")
1275
+
1276
+ def set_low_output_mode(self):
1277
+ # alias for mode()
1278
+ return self.mode("low", "output")
1279
+
1280
+ def set_high_input_mode(self):
1281
+ # alias for mode()
1282
+ return self.mode("high", "input")
1283
+
1284
+ def set_high_output_mode(self):
1285
+ # alias for mode()
1286
+ # TODO: ERROR CHECKING
1287
+ return self.mode("high", "output")
1288
+
1289
+ def modulation(self, val):
1290
+ # sets the modulation in output mode
1291
+ # usage: modulation off|AM_1kHz|AM_10Hz|NFM|WFM|extern
1292
+ # example return: ''
1293
+
1294
+ #explicitly allowed vals
1295
+ accepted_vals = ["off", "AM_1kHz", "AM_10Hz",
1296
+ "NFM", "WFM", "extern"]
1297
+ #check input
1298
+ if (str(val) in accepted_vals):
1299
+ writebyte = 'output '+str(val)+'\r\n'
1300
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
1301
+ else:
1302
+ self.print_message("ERROR: output() takes vals [on|off]")
1303
+ msgbytes = self.error_byte_return()
1304
+ return msgbytes
1305
+
1306
+ def set_mod_off(self):
1307
+ # alias for modulation()
1308
+ return self.modulation("off")
1309
+ def set_mod_AM_1khz(self):
1310
+ # alias for modulation()
1311
+ return self.modulation("AM_1kHz")
1312
+ def set_mod_AM_10Hz(self):
1313
+ # alias for modulation()
1314
+ return self.modulation("AM_10Hz")
1315
+ def set_mod_NFM(self):
1316
+ # alias for modulation()
1317
+ return self.modulation("NFM")
1318
+ def set_mod_WFM(self):
1319
+ # alias for modulation()
1320
+ return self.modulation("WFM")
1321
+ def set_mod_extern(self):
1322
+ # alias for modulation()
1323
+ return self.modulation("extern")
1324
+
1325
+
1326
+ def nf(self):
1327
+ # get the noise floor in dB.
1328
+ # This function CAN be used to set nf,
1329
+ # but that might bypass a measurement process. UNKNOWN right now.
1330
+ # usage: nf {value}\r\n
1331
+ # example return: ''
1332
+ writebyte = 'nf\r\n'
1333
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
1334
+ self.print_message("getting saved noise floor value")
1335
+ return msgbytes
1336
+
1337
+ def get_nf(self):
1338
+ # alias function for nf()
1339
+ return self.nf()
1340
+
1341
+ def output(self, val):
1342
+ # sets the output on or off
1343
+ # usage: output on|off
1344
+ # example return: ''
1345
+
1346
+ # explicitly allowed vals
1347
+ accepted_vals = ["on", "off"]
1348
+ #check input
1349
+ if (str(val) in accepted_vals):
1350
+ writebyte = 'output '+str(val)+'\r\n'
1351
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
1352
+ else:
1353
+ self.print_message("ERROR: output() takes vals [on|off]")
1354
+ msgbytes = self.error_byte_return()
1355
+ return msgbytes
1356
+
1357
+ def set_output_on(self):
1358
+ #alias for output()
1359
+ return self.output("on")
1360
+ def set_output_off(self):
1361
+ #alias for output()
1362
+ return self.output("off")
1363
+
1364
+
1365
+ def pause(self):
1366
+ # pauses the sweeping in either input or output mode
1367
+ # usage: pause
1368
+ # example return: ''
1369
+
1370
+ writebyte = 'pause\r\n'
1371
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
1372
+ self.print_message("pausing tinySA device")
1373
+ return msgbytes
1374
+
1375
+ def rbw(self, val="auto"):
1376
+ # sets the rbw to either automatic or a specific value.
1377
+ # the number specifies the target rbw in kHz
1378
+ # usage: rbw auto|3..600
1379
+ # example return: ''
1380
+
1381
+ #check input
1382
+ if (val == "auto"):
1383
+ writebyte = 'rbw '+str(val)+'\r\n'
1384
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
1385
+ elif (isinstance(val, int)):
1386
+ writebyte = 'rbw '+str(val)+'\r\n'
1387
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
1388
+ else:
1389
+ self.print_message("ERROR: rbw() takes vals [auto |0 - 600] in kHz as integers")
1390
+ msgbytes = self.error_byte_return()
1391
+ return msgbytes
1392
+
1393
+ def set_rbw_auto(self):
1394
+ # alias for rbw()
1395
+ return self.rbw("auto")
1396
+
1397
+
1398
+ def recall(self, val=0):
1399
+ # loads a previously stored preset,where 0 is the startup preset
1400
+ # usage: recall [0-4]
1401
+ # example return: ''
1402
+
1403
+ #explicitly allowed vals
1404
+ accepted_vals = [0,1,2,3,4]
1405
+ #check input
1406
+ if (val in accepted_vals):
1407
+ writebyte = 'recall '+str(val)+'\r\n'
1408
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
1409
+ self.print_message("recall() set to value " + str(val))
1410
+ else:
1411
+ self.print_message("ERROR: recall() takes vals [0 - 4]")
1412
+ msgbytes = self.error_byte_return()
1413
+ return msgbytes
1414
+
1415
+ def refresh(self, val):
1416
+ # enables/disables the auto refresh mode
1417
+ # usage: refresh on|off
1418
+ # example return: ''
1419
+
1420
+ #explicitly allowed vals
1421
+ accepted_vals = ["on", "off"]
1422
+ #check input
1423
+ if (str(val) in accepted_vals):
1424
+ writebyte = 'refresh '+str(val)+'\r\n'
1425
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
1426
+ self.print_message("refresh() set to " + str(val))
1427
+ else:
1428
+ self.print_message("ERROR: refresh() takes vals [on|off]")
1429
+ msgbytes = self.error_byte_return()
1430
+ return msgbytes
1431
+
1432
+ def refresh_on(self):
1433
+ # alias for refresh()
1434
+ return self.refresh("on")
1435
+
1436
+ def refresh_off(self):
1437
+ # alias for refresh()
1438
+ return self.refresh("off")
1439
+
1440
+ def release(self):
1441
+ # signals a removal of the touch
1442
+ # usage: release
1443
+ # example return: bytearray(b'')
1444
+
1445
+ writebyte = 'release\r\n'
1446
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
1447
+ self.print_message("sending touch release signal")
1448
+ return msgbytes
1449
+
1450
+ def remark(self, val):
1451
+ # does nothing
1452
+ # usage: remark {any text}
1453
+ # example return: bytearray(b'')
1454
+ writebyte = 'remark ' + str(val) + '\r\n'
1455
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
1456
+ self.print_message("remark " + str(val))
1457
+
1458
+ return msgbytes
1459
+
1460
+ def repeat(self, val=1):
1461
+ # Sets the number of (re)measurements that
1462
+ # should be taken at every frequency
1463
+ # usage: repeat
1464
+ # example return: bytearray(b'')
1465
+
1466
+ val = int(val)
1467
+ if (1<=val<=1000):
1468
+ writebyte = 'repeat ' + str(val) + '\r\n'
1469
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
1470
+ self.print_message("setting the repeat() measurement to " + str(val))
1471
+ else:
1472
+ self.print_message("ERROR: repeat() takes integer vals [0 - 1000]")
1473
+ msgbytes = self.error_byte_return()
1474
+ return msgbytes
1475
+
1476
+ def reset(self):
1477
+ # reset the tinySA Ultra. NOTE: will disconnect and fully reset
1478
+ # usage: reset
1479
+ # example return: throws error. raise SerialException
1480
+
1481
+ writebyte = 'reset\r\n'
1482
+ self.print_message("sending reset signal. Serial will disconnect...")
1483
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
1484
+ return msgbytes
1485
+
1486
+ def reset_device(self):
1487
+ # alias function for reset()
1488
+ return self.reset()
1489
+
1490
+
1491
+ def restart(self, val=0):
1492
+ # restarts the tinySA after the specified number of seconds
1493
+ # usage: restart {seconds}
1494
+ # example return: ''
1495
+ val = int(val)
1496
+ if val == 0:
1497
+ writebyte = 'restart ' + str(val) + '\r\n'
1498
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
1499
+ self.print_message("restarting cancelled.")
1500
+ elif (0<val):
1501
+ writebyte = 'restart ' + str(val) + '\r\n'
1502
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
1503
+ self.print_message("restarting the device in " + str(val) + " seconds.")
1504
+ else:
1505
+ self.print_message("ERROR: restart() takes vals 0 or greater")
1506
+ msgbytes = self.error_byte_return()
1507
+
1508
+ return msgbytes
1509
+
1510
+ def restart_device(self, val):
1511
+ # alias function for restart
1512
+ return self.restart(val)
1513
+ def cancel_restart(self):
1514
+ # alias function for restart
1515
+ return self.restart(val=0)
1516
+
1517
+
1518
+ def resume(self):
1519
+ # resumes the sweeping in either input or output mode
1520
+ # usage: resume
1521
+ # example return: ''
1522
+
1523
+ writebyte = 'resume\r\n'
1524
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
1525
+ self.print_message("resuming sweep")
1526
+ return msgbytes
1527
+
1528
+ def save(self, val=1):
1529
+ # saves the current setting to a preset, where 0 is the startup preset
1530
+ # usage: save [0-4]
1531
+ # example return: ''
1532
+
1533
+ #explicitly allowed vals
1534
+ accepted_vals = [0,1,2,3,4]
1535
+ #check input
1536
+ if (val in accepted_vals):
1537
+ writebyte = 'save '+str(val)+'\r\n'
1538
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
1539
+ self.print_message("saving to preset " + str(val))
1540
+ else:
1541
+ self.print_message("ERROR: save() takes vals [0 - 4] as integers")
1542
+ msgbytes = self.error_byte_return()
1543
+ return msgbytes
1544
+
1545
+ def save_config(self):
1546
+ # saves the device configuration data
1547
+ # usage: saveconfig
1548
+ # example return: bytearray(b'Config saved.\r')
1549
+
1550
+ writebyte = 'saveconfig\r\n'
1551
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
1552
+ self.print_message("save_config() called")
1553
+ return msgbytes
1554
+
1555
+ def scan(self, start, stop, pts=250, outmask=None):
1556
+ # Performs a scan and optionally outputs the measured data.
1557
+ # usage: scan {start(Hz)} {stop(Hz)} [points] [outmask]
1558
+ # where the outmask is a binary OR of:
1559
+ # 1=frequencies, 2=measured data,
1560
+ # 4=stored data and max points is device dependent
1561
+
1562
+ if (0<=start) and (start < stop) and (pts <= self.maxPoints):
1563
+ if outmask == None:
1564
+ writebyte = 'scan '+str(start)+' '+str(stop)+' '+str(pts)+'\r\n'
1565
+ else:
1566
+ writebyte = 'scan '+str(start)+' '+str(stop)+' '+str(pts)+ ' '+str(outmask)+'\r\n'
1567
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
1568
+ self.print_message("scanning...")
1569
+ else:
1570
+ self.print_message("ERROR: scan takes START STOP PTS OUTMASK as args. Check doc for format and limits")
1571
+ msgbytes = self.error_byte_return()
1572
+ return msgbytes
1573
+
1574
+
1575
+ def scan_raw(self, start, stop, pts=250, unbuf=1):
1576
+ # performs a scan of unlimited amount of points
1577
+ # and sends the data in binary form
1578
+ # usage: scanraw {start(Hz)} {stop(Hz)} [points] [unbuffered]
1579
+ # The measured data is sent as:
1580
+ # '{' ('x' MSB LSB)*points '}'
1581
+ # where the 16 bit data is scaled by 32 & shifted based on device.
1582
+ # the README has examples for processing
1583
+
1584
+ if (0<=start) and (start < stop) and (pts <= self.maxPoints):
1585
+ if (unbuf == 1) or (unbuf==2) or (unbuf==3):
1586
+ writebyte = 'scanraw '+str(start)+' '+str(stop)+' '+str(pts)+ ' '+str(unbuf)+'\r\n'
1587
+
1588
+ # write out to serial, get message back, clean up, return
1589
+ self.print_message("scanning...")
1590
+ msgbytes = self.tinySA_serial(writebyte, printBool=False, pts=pts) #pts added for error checking
1591
+ return msgbytes
1592
+ else:
1593
+ self.print_message("ERROR: unrecognized UBUF for scanraw")
1594
+ msgbytes = self.error_byte_return()
1595
+ else:
1596
+ self.print_message("ERROR: scanraw takes START STOP PTS UNBUF as args. Check doc for format and limits")
1597
+ msgbytes = self.error_byte_return()
1598
+ return msgbytes
1599
+
1600
+ def continious_scanraw(self):
1601
+ pass # the continious scan might need to be handled differently
1602
+
1603
+ def sd_delete(self, val):
1604
+ # delete a specific file on the sd card
1605
+ # usage: sd_delete {filename}
1606
+ # example return:
1607
+
1608
+ writebyte = 'sd_delete ' + str(val)+ '\r\n'
1609
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
1610
+ self.print_message("deleting file from sd card")
1611
+ return msgbytes
1612
+
1613
+
1614
+ def sd_list(self):
1615
+ # displays list of filenames with extension and sizes
1616
+ # usage: sd_list
1617
+ # example return: bytearray(b'-0.bmp 307322\r')
1618
+
1619
+ writebyte = 'sd_list\r\n'
1620
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
1621
+ self.print_message("listing files from sd card")
1622
+ return msgbytes
1623
+
1624
+ def sd_read(self, val):
1625
+ # read a specific file on the sd_card
1626
+ # usage: sd_read {filename}
1627
+ # example return:
1628
+
1629
+ writebyte = 'sd_read ' + str(val)+ '\r\n'
1630
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
1631
+ self.print_message("reading file from sd card")
1632
+ return msgbytes
1633
+
1634
+ def self_test(self, val=0):
1635
+ # performs one or all selftests
1636
+ # usage: selftest 0 0..9.
1637
+ # 0 appears to be 'run all'
1638
+ # example return: msgbytes = bytearray(b'')
1639
+
1640
+ #check input
1641
+ if (isinstance(val, int)):
1642
+ writebyte = 'selftest ' + str(val) + '\r\n'
1643
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
1644
+ self.print_message("SELFTEST RUNNING. CHECK CONNECTION CAL to RF")
1645
+ else:
1646
+ self.print_message("ERROR: self_test() takes interger vals. 0 to run all.")
1647
+ msgbytes = self.error_byte_return()
1648
+ return msgbytes
1649
+
1650
+ def spur(self, val):
1651
+ # enables or disables spur reduction
1652
+ # usage: spur on|off
1653
+ # example return:
1654
+
1655
+ # explicitly allowed vals
1656
+ accepted_vals = ["on", "off"]
1657
+ #check input
1658
+ if (str(val) in accepted_vals):
1659
+ writebyte = 'spur '+str(val)+'\r\n'
1660
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
1661
+ self.print_message("spur() set to " + str(val))
1662
+ else:
1663
+ self.print_message("ERROR: spur() takes vals [on|off]")
1664
+ msgbytes = self.error_byte_return()
1665
+ return msgbytes
1666
+
1667
+ def spur_on(self):
1668
+ # alias for spur()
1669
+ return self.spur("on")
1670
+ def spur_off(self):
1671
+ # alias for spur()
1672
+ return self.spur("off")
1673
+
1674
+ def status(self):
1675
+ # displays the current device status (paused/resumed)
1676
+ # usage: status
1677
+ # example return: bytearray(b'Resumed\r')
1678
+
1679
+ writebyte = 'status\r\n'
1680
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
1681
+ self.print_message("getting device status() paused/resumed")
1682
+ return msgbytes
1683
+
1684
+ def get_status(self):
1685
+ # alias for status()
1686
+ return self.get_status()
1687
+
1688
+
1689
+ def config_sweep(self, argName=None, val=None):
1690
+ # split call for SWEEP
1691
+ # Set sweep boundaries.
1692
+ # Sweep without arguments lists the current sweep
1693
+ # settings. The frequencies specified should be
1694
+ # within the permissible range. The sweep commands
1695
+ # apply both to input and output modes
1696
+ # usage:
1697
+ # sweep [(start|stop|center|span|cw {frequency}) |
1698
+ # ({start(Hz)} {stop(Hz)} [0..290])]
1699
+ # EXAMPLES:
1700
+ # sweep start {frequency}: sets the start frequency of the sweep.
1701
+ # sweep stop {frequency}: sets the stop frequency of the sweep.
1702
+ # sweep center {frequency}: sets the center frequency of the sweep.
1703
+ # sweep span {frequency}: sets the span of the sweep.
1704
+ # sweep cw {frequency}: sets the continuous wave frequency (zero span sweep).
1705
+ # # example return: b''
1706
+
1707
+ # explicitly allowed vals
1708
+ accepted_table_args = ["start", "stop", "center",
1709
+ "span", "cw"]
1710
+
1711
+ if (argName==None) and (val==None):
1712
+ # do sweep
1713
+ writebyte = 'sweep\r\n'
1714
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
1715
+
1716
+ elif (argName in accepted_table_args):
1717
+ if val == None:
1718
+ #error
1719
+ self.print_message("ERROR: sweep " + str(argName) + " needs a value")
1720
+ msgbytes = self.error_byte_return()
1721
+ else:
1722
+ #do stuff, error checking needed
1723
+ writebyte = 'sweep ' + str(argName)+ ' ' + str(val)+ '\r\n'
1724
+ self.print_message("sweep " +str(argName) + " is " + str(val))
1725
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
1726
+
1727
+ else: #not in table of accepted args, so doesn't matter what val is
1728
+ self.print_message("ERROR: " + str(argName) + " invalid argument for sweep")
1729
+ msgbytes = self.error_byte_return()
1730
+
1731
+ return msgbytes
1732
+
1733
+ def get_sweep_params(self):
1734
+ # alias for config_sweep()
1735
+ return self.config_sweep()
1736
+ def set_sweep_start(self, val):
1737
+ # alias for config_sweep()
1738
+ return self.config_sweep("start", val)
1739
+ def set_sweep_stop(self, val):
1740
+ # alias for config_sweep()
1741
+ return self.config_sweep("stop", val)
1742
+ def set_sweep_center(self, val):
1743
+ # alias for config_sweep()
1744
+ return self.config_sweep("center", val)
1745
+ def set_sweep_span(self, val):
1746
+ # alias for config_sweep()
1747
+ return self.config_sweep("span", val)
1748
+ def set_sweep_cw(self, val):
1749
+ # alias for config_sweep()
1750
+ return self.config_sweep("cw", val)
1751
+
1752
+ def run_sweep(self, startVal=None, stopVal=None, pts=250):
1753
+ # split call for SWEEP
1754
+ # Execute sweep.
1755
+ # The frequencies specified should be
1756
+ # within the permissible range. The sweep commands
1757
+ # apply both to input and output modes
1758
+ # usage:
1759
+ # sweep [(start|stop|center|span|cw {frequency}) |
1760
+ # ({start(Hz)} {stop(Hz)} [0..290])]
1761
+ # # example return:
1762
+ if (startVal==None) or (stopVal==None):
1763
+ self.print_message("ERROR: sweep start and stop need non-empty values")
1764
+ msgbytes = self.error_byte_return()
1765
+ elif (int(startVal) >= int(stopVal)):
1766
+ self.print_message("ERROR: sweep start must be less than sweep stop value")
1767
+ msgbytes = self.error_byte_return()
1768
+ else:
1769
+ #do stuff, error checking needed
1770
+ self.print_message("sweeping...")
1771
+ writebyte = 'sweep '+str(startVal)+' '+str(stopVal)+' '+str(pts)+'1\r\n'
1772
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
1773
+
1774
+ return msgbytes
1775
+
1776
+ def sweep_time(self, val):
1777
+ # sets the sweeptime
1778
+ # usage: sweep {time(Seconds)}the time
1779
+ # specified may end in a letter where
1780
+ # m=mili and u=micro
1781
+ # example return: b''
1782
+
1783
+
1784
+ # needs some error checking
1785
+
1786
+ writebyte = 'sweeptime '+str(val)+'\r\n'
1787
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
1788
+ self.print_message("sweeptime set to " + str(val))
1789
+ return msgbytes
1790
+
1791
+ def temp(self):
1792
+ # gets the temperature
1793
+ # usage: k (NOTE: single letter command)
1794
+ # example return:
1795
+ # b'43.25\r'
1796
+ writebyte = 'k\r\n'
1797
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
1798
+ self.print_message("getting temperature")
1799
+ return msgbytes
1800
+
1801
+ def get_temp(self):
1802
+ # alias for temp()
1803
+ return self.temp()
1804
+
1805
+
1806
+ def text(self, val=""):
1807
+ # specifies the text entry for the active keypad
1808
+ # usage: text(val="")
1809
+ # example return: b''
1810
+
1811
+ if len(str(val))>0:
1812
+ writebyte = 'text ' + str(val) +'\r\n'
1813
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
1814
+ self.print_message("text() entered is " + str(val))
1815
+ else:
1816
+ self.print_message("ERROR: text needs non-empty values")
1817
+ msgbytes = self.error_byte_return()
1818
+ return msgbytes
1819
+
1820
+ def threads(self):
1821
+ # lists information of the threads in the tinySA
1822
+ # usage: threads
1823
+ # example return:
1824
+ # bytearray(b'stklimit| ...\r')
1825
+
1826
+ writebyte = 'threads\r\n'
1827
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
1828
+ self.print_message("returning thread information for device")
1829
+ return msgbytes
1830
+
1831
+ def touch(self, x=0, y=0):
1832
+ # sends the coordinates of a touch.
1833
+ # The upper left corner of the screen is 0 0
1834
+ # usage: touch {X coordinate} {Y coordinate}
1835
+ # example return:
1836
+
1837
+ # check if valid x
1838
+ if (x<0) or (self.screenWidth<x):
1839
+ self.print_message("ERROR: touch() needs a valid x coordinate")
1840
+ msgbytes = self.error_byte_return()
1841
+ return msgbytes
1842
+ # check if valid y
1843
+ if (y<0) or (self.screenHeight<y):
1844
+ self.print_message("ERROR: touch() needs a valid y coordinate")
1845
+ msgbytes = self.error_byte_return()
1846
+ return msgbytes
1847
+ writebyte = 'touch ' + str(x) + ' ' + str(y) + '\r\n'
1848
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
1849
+ self.print_message("setting the touch() (" + str(x)+"," + str(y) + ")")
1850
+ return msgbytes
1851
+
1852
+ def preform_touch(self, x, y):
1853
+ #alias for touch()
1854
+ return self.touch(x,y)
1855
+
1856
+
1857
+ def touch_cal(self):
1858
+ # starts the touch calibration
1859
+ # usage: touchcal
1860
+ # example return: bytearray(b'')
1861
+ writebyte = 'touchcal\r\n'
1862
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
1863
+ self.print_message("starting touchcal")
1864
+ return msgbytes
1865
+
1866
+ def start_touch_cal(self):
1867
+ return self.touch_cal()
1868
+
1869
+ def touch_test(self):
1870
+ # starts the touch test
1871
+ # usage: touchtest
1872
+ # example return: bytearray(b'')
1873
+
1874
+ writebyte = 'touchtest\r\n'
1875
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
1876
+ self.print_message("starting the touch_test()")
1877
+ return msgbytes
1878
+
1879
+ def start_touch_test(self):
1880
+ return self.touch_test()
1881
+
1882
+ def trace_select(self, ID):
1883
+ # split call for TRACE. select an available trace
1884
+ if (isinstance(ID, int)) and ID >=0:
1885
+ writebyte = 'trace '+ str(ID) +'\r\n'
1886
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
1887
+ self.print_message("selecting trace")
1888
+ else:
1889
+ self.print_message("ERROR: trace numbers must be integers greater than 0. see device documentation for max")
1890
+ msgbytes = self.error_byte_return()
1891
+ return msgbytes
1892
+
1893
+ def trace_units(self, val):
1894
+ # split call for TRACE. set the units for the traces
1895
+ # explicitly allowed vals
1896
+ accepted_vals = ["dBm", "dBmV", "dBuV", "V", "W", "Vpp", "RAW"]
1897
+
1898
+ if (str(val) in accepted_vals):
1899
+ writebyte = 'trace '+ str(val) +'\r\n'
1900
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
1901
+ self.print_message("setting trace units to " + str(val))
1902
+ else:
1903
+ self.print_message("ERROR: trace vals can be 'dBm'|'dBmV'|'dBuV'|'RAW'|'V'|'Vpp'|'W'")
1904
+ msgbytes = self.error_byte_return()
1905
+ return msgbytes
1906
+
1907
+ def trace_scale(self, val="auto"):
1908
+ # split call for TRACE. scales a trace/traces.
1909
+ writebyte = 'trace scale' + str(val) + '\r\n'
1910
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
1911
+ self.print_message("scaling trace")
1912
+ return msgbytes
1913
+
1914
+ def trace_reflevel(self, val="auto"):
1915
+ # split call for TRACE. sets the reference level of a trace
1916
+ writebyte = 'trace reflevel' + str(val) + '\r\n'
1917
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
1918
+ self.print_message("setting reference level of trace")
1919
+ return msgbytes
1920
+
1921
+ def trace_value(self, ID):
1922
+ # split call for TRACE. gets values of trace
1923
+
1924
+ writebyte = 'trace' + str(ID) + 'value \r\n'
1925
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
1926
+ self.print_message("getting raw trace values")
1927
+ return msgbytes
1928
+
1929
+ def trace_toggle(self, ID, val="on"):
1930
+ # split call for TRACE. toggle trace ON or OFF
1931
+ # full description: displays all or one trace information
1932
+ # or sets trace related information
1933
+ # usage:
1934
+ # trace [ {0..2} |
1935
+ # dBm|dBmV|dBuV|V|W |store|clear|subtract | (scale|
1936
+ # reflevel) auto|{level}
1937
+ # example return:
1938
+
1939
+ accepted_vals = ["on", "off"]
1940
+
1941
+ if (isinstance(ID,int)) and (str(val) in accepted_vals):
1942
+ writebyte = 'trace' + str(ID) + ' ' +str(val)+ '\r\n'
1943
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
1944
+ self.print_message("toggling trace " +str(val))
1945
+ else:
1946
+ self.print_message("ERROR: trace ID is an Int, val='on'|'off'")
1947
+ msgbytes = self.error_byte_return()
1948
+
1949
+ return msgbytes
1950
+
1951
+ def trace_subtract(self, ID1, ID2):
1952
+ # split call for TRACE. subtracts a trace/traces.
1953
+ # subtract ID1 FROM ID2
1954
+
1955
+ if (isinstance(ID1,int)) and (isinstance(ID2,int)):
1956
+ writebyte = 'trace' + str(ID1) + ' subtract ' +str(ID2)+ '\r\n'
1957
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
1958
+ self.print_message("subtracting traces")
1959
+ else:
1960
+ self.print_message("ERROR: trace IDs must be Ints")
1961
+ msgbytes = self.error_byte_return()
1962
+
1963
+ return msgbytes
1964
+
1965
+ def trace_copy(self, ID1, ID2):
1966
+ # split call for TRACE. copies a trace/traces.
1967
+
1968
+ if (isinstance(ID1,int)) and (isinstance(ID2,int)):
1969
+ writebyte = 'trace' + str(ID1) + ' subtract ' +str(ID2)+ '\r\n'
1970
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
1971
+ self.print_message("copying traces")
1972
+ else:
1973
+ self.print_message("ERROR: trace IDs must be Ints")
1974
+ msgbytes = self.error_byte_return()
1975
+
1976
+ return msgbytes
1977
+
1978
+ def trace_freeze(self, ID):
1979
+ # split call for TRACE. freezes a trace
1980
+
1981
+ if (isinstance(ID,int)):
1982
+ writebyte = 'trace' + str(ID) + ' freeze\r\n'
1983
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
1984
+ self.print_message("freezing trace")
1985
+ else:
1986
+ self.print_message("ERROR: trace ID must be Ints")
1987
+ msgbytes = self.error_byte_return()
1988
+
1989
+ return msgbytes
1990
+
1991
+
1992
+ def trace_clear(self, val):
1993
+ # split call for TRACE. clears a trace/traces. doesnt seem to take inputs
1994
+ # full description: displays all or one trace information
1995
+ # or sets trace related information
1996
+ # usage:
1997
+ # trace [ {0..2} |
1998
+ # dBm|dBmV|dBuV|V|W |store|clear|subtract | (scale|
1999
+ # reflevel) auto|{level}
2000
+ # example return:
2001
+
2002
+ writebyte = 'trace ' + str(val) + 'clear \r\n'
2003
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
2004
+ self.print_message("clearing trace(s)")
2005
+ return msgbytes
2006
+
2007
+
2008
+
2009
+ def trace_freeze(self, ID):
2010
+ # split call for TRACE. sets the reference level of a trace
2011
+ # full description: displays all or one trace information
2012
+ # or sets trace related information
2013
+ # usage:
2014
+ # trace [ {0..2} |
2015
+ # dBm|dBmV|dBuV|V|W |store|clear|subtract | (scale|
2016
+ # reflevel) auto|{level}
2017
+ # example return:
2018
+
2019
+ writebyte = 'trace' + str(ID) + 'freeze \r\n'
2020
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
2021
+ self.print_message("freezing trace")
2022
+ return msgbytes
2023
+
2024
+
2025
+
2026
+ def trace_action(self, ID, val):
2027
+ # split call for TRACE. toggle trace ON or OFF
2028
+ # full description: displays all or one trace information
2029
+ # or sets trace related information
2030
+ # usage:
2031
+ # trace [ {0..2} |
2032
+ # dBm|dBmV|dBuV|V|W |store|clear|subtract | (scale|
2033
+ # reflevel) auto|{level}
2034
+ # example return:
2035
+
2036
+ accepted_vals = ["copy","freeze","subtract","view","value"]
2037
+
2038
+ if (isinstance(ID,int)) and (str(val) in accepted_vals):
2039
+ writebyte = 'trace' + str(ID) + ' ' +str(val)+ '\r\n'
2040
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
2041
+ self.print_message("setting trace action")
2042
+ else:
2043
+ self.print_message("ERROR: trace vals can be 'copy'|'freeze'|'subtract'|'view'|'value' and ID is an Int")
2044
+ msgbytes = self.error_byte_return()
2045
+
2046
+ return msgbytes
2047
+
2048
+
2049
+
2050
+ def trigger(self, val, freq=None):
2051
+ # sets the trigger type or level
2052
+ # usage: trigger auto|normal|single|{level(dBm)}
2053
+ # the trigger level is always set in dBm and is the only numerical input
2054
+ # example return:
2055
+ # #explicitly allowed vals
2056
+ accepted_vals = ["auto", "normal", "single"]
2057
+
2058
+ if str(val) in accepted_vals:
2059
+ writebyte = 'trigger ' + str(val) +'\r\n'
2060
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
2061
+ self.print_message("setting trigger to " + str(val))
2062
+ elif val==None and isinstance(freq,int):
2063
+ writebyte = 'trigger ' + str(freq) +'\r\n'
2064
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
2065
+ self.print_message("setting trigger level (dBm) to " + str(freq))
2066
+ else:
2067
+ self.print_message("ERROR: trigger takes inputs auto|normal|single|{level(dBm)}")
2068
+ msgbytes = self.error_byte_return()
2069
+ return msgbytes
2070
+
2071
+ def trigger_auto(self):
2072
+ # alias for trigger
2073
+ return self.trigger("auto")
2074
+ def trigger_normal(self):
2075
+ # alias for trigger
2076
+ return self.trigger("normal")
2077
+ def trigger_single(self):
2078
+ # alias for trigger
2079
+ return self.trigger("single")
2080
+ def trigger_level(self, val):
2081
+ # alias for trigger
2082
+ return self.trigger(None, val)
2083
+
2084
+ def ultra(self, val="off", freq=None):
2085
+ # turn on/config tiny SA ultra mode
2086
+ # usage: ultra off|on|auto|start|harm {freq}
2087
+ # example return: bytearray(b'')
2088
+
2089
+ # explicitly allowed vals
2090
+ accepted_vals = ["off", "on", "auto", "start", "harm"]
2091
+
2092
+ if str(val) in ["off", "on", "auto"]:
2093
+ writebyte = 'ultra ' + str(val) +'\r\n'
2094
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
2095
+ self.print_message("configuring ultra() " + str(val))
2096
+ elif str(val) in ["start", "harm"]:
2097
+ writebyte = 'ultra ' + str(val) + ' ' + str(freq) +'\r\n'
2098
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
2099
+ self.print_message("configuring ultra() " + str(val) + " at " + str(freq))
2100
+ else:
2101
+ self.print_message("ERROR: ultra() currently only takes on/off as args")
2102
+ msgbytes = self.error_byte_return()
2103
+
2104
+ return msgbytes
2105
+
2106
+ def set_ultra_on(self):
2107
+ return self.ultra("on")
2108
+
2109
+ def set_ultra_off(self):
2110
+ return self.ultra("off")
2111
+
2112
+ def set_ultra_auto(self):
2113
+ return self.ultra("auto")
2114
+
2115
+ def set_ultra_start(self, val):
2116
+ return self.ultra("start", val)
2117
+
2118
+ def set_ultra_harmonic(self, val):
2119
+ return self.ultra("harm", val)
2120
+
2121
+ def usart_cfg(self):
2122
+ # gets the current serial config
2123
+ # usage: usart_cfg
2124
+ # example return: bytearray(b'Serial: 115200 baud\r')
2125
+
2126
+ writebyte = 'usart_cfg\r\n'
2127
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
2128
+ self.print_message("usart_cfg() returning config vals")
2129
+ return msgbytes
2130
+
2131
+ def get_usart_cfg(self):
2132
+ #alias for usart_cfg()
2133
+ return self.usart_cfg()
2134
+
2135
+
2136
+ def vbat(self):
2137
+ # displays the battery voltage
2138
+ # usage: vbat
2139
+ # example return: bytearray(b'4132 mV\r')
2140
+ writebyte = 'vbat\r\n'
2141
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
2142
+ self.print_message("returning current battery voltage")
2143
+ return msgbytes
2144
+
2145
+ def get_vbat(self):
2146
+ # alias for vbat
2147
+ return self.vbat()
2148
+
2149
+ def vbat_offset(self, val=None):
2150
+ # displays or sets the battery offset value
2151
+ # usage: vbat_offset [{0..4095}]
2152
+ # example return: bytearray(b'300\r')
2153
+
2154
+ if val == None:
2155
+ #get the offset
2156
+ writebyte = 'vbat_offset\r\n'
2157
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
2158
+ elif (isinstance(val, (int, float))) and (0<= val <=4095):
2159
+ writebyte = 'vbat_offset '+str(val)+'\r\n'
2160
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
2161
+ self.print_message("vbat_offset set to " + str(val))
2162
+ else:
2163
+ self.print_message("ERROR: vbat_offset() takes either None or [0 - 4095] integers")
2164
+ msgbytes = self.error_byte_return()
2165
+ return msgbytes
2166
+
2167
+ def get_vbat_offset(self):
2168
+ # alias for vbat_offset()
2169
+ return self.vbat_offset()
2170
+ def set_vbat_offset(self, val):
2171
+ # alias for vbat_offset()
2172
+ return self.vbat_offset(val)
2173
+
2174
+
2175
+ def version(self):
2176
+ # displays the version text
2177
+ # usage: version
2178
+ # example return: tinySA4_v1.4-143-g864bb27\r\nHW Version:V0.4.5.1.1
2179
+
2180
+ writebyte = 'version\r\n'
2181
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
2182
+ self.print_message("getting device version information")
2183
+ return msgbytes
2184
+
2185
+ def get_version(self):
2186
+ # alias for version()
2187
+ return self.version()
2188
+
2189
+ def wait(self, val=0):
2190
+ # wait for a single sweep to finish and pauses
2191
+ # sweep or waits for specified number of seconds
2192
+ # usage: wait [{seconds}]
2193
+ # example return:
2194
+
2195
+ if val == None:
2196
+ writebyte = 'wait\r\n'
2197
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
2198
+ self.print_message("device in wait() state. manually resume")
2199
+ elif val>0:
2200
+ writebyte = 'wait ' + str(val) + '\r\n'
2201
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
2202
+ self.print_message("device wait() trigged for " + str(val) + " seconds.")
2203
+ else:
2204
+ self.print_message("ERROR: wait() takes None or positive ints")
2205
+ msgbytes = self.error_byte_return()
2206
+ return msgbytes
2207
+
2208
+ def zero(self, val):
2209
+ #get or set the zero offset in dBm
2210
+ # DO NOT CHANGE if unfamiliar with device and offset
2211
+ # usage: zero {level}\r\n174dBm
2212
+ # example return:
2213
+
2214
+ if val == None:
2215
+ writebyte = 'zero\r\n'
2216
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
2217
+ self.print_message("returning zero offset")
2218
+ else:
2219
+ writebyte = 'zero ' + str(val) + '\r\n'
2220
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
2221
+ self.print_message("device zero offset is " + str(val) + " dBm.")
2222
+
2223
+ return msgbytes
2224
+
2225
+ def get_zero_offset(self):
2226
+ # alias function for zero
2227
+ return self.zero()
2228
+
2229
+
2230
+ ######################################################################
2231
+ # Device and library help
2232
+ ######################################################################
2233
+
2234
+ def help(self, val=0):
2235
+ # val controls if the tinySA help is called or the
2236
+ # 1 = library_help(), everything else is the tinySA_help()
2237
+
2238
+ if val == 1:
2239
+ msgbytes = self.library_help()
2240
+ else:
2241
+ msgbytes = self.tinySA_help()
2242
+ return msgbytes
2243
+
2244
+ def library_help(self):
2245
+ self.print_message("Returning command options for this library")
2246
+ self.print_message("IN PROGRESS. Include tinySA_help.py")
2247
+
2248
+ return b''
2249
+
2250
+ def tinySA_help(self):
2251
+ # dumps a list of the available commands
2252
+ # usage: help
2253
+ # example return: bytearray(b'commands: freq time dac
2254
+ # nf saveconfig clearconfig zero sweep pause resume wait
2255
+ # repeat status caloutput save recall trace trigger
2256
+ # marker line usart_cfg vbat_offset color if if1 lna2
2257
+ # agc actual_freq freq_corr attenuate level sweeptime
2258
+ # leveloffset levelchange modulation rbw mode spur
2259
+ # lna direct ultra load ext_gain output deviceid
2260
+ # correction calc menu text remark\r\nOther commands:
2261
+ # version reset data frequencies scan hop scanraw test
2262
+ # touchcal touchtest usart capture refresh touch release
2263
+ # vbat help info selftest sd_list sd_read sd_delete
2264
+ # threads\r')
2265
+
2266
+ writebyte = 'help\r\n'
2267
+ msgbytes = self.tinySA_serial(writebyte, printBool=False)
2268
+ self.print_message("Returning command options for tinySA device")
2269
+ return msgbytes
2270
+
2271
+
2272
+
2273
+ ######################################################################
2274
+ # Device Selection and Config Functions
2275
+ # TODO LATER
2276
+ # This is a quick template. there's more options that need to
2277
+ # be researched
2278
+ ######################################################################
2279
+
2280
+
2281
+
2282
+
2283
+ ######################################################################
2284
+ # Unit testing
2285
+ ######################################################################
2286
+
2287
+ if __name__ == "__main__":
2288
+ # unit testing. not recomended to write program from here
2289
+
2290
+ # create a new tinySA object
2291
+ tsa = tinySA()
2292
+ # attempt to connect to previously discovered serial port
2293
+ #success = tsa.connect(port='COM10')
2294
+
2295
+ # attempt to autoconnect
2296
+ found_bool, connected_bool = tsa.autoconnect()
2297
+
2298
+ # if port open, then complete task(s) and disconnect
2299
+ if connected_bool == True: # or if success == True:
2300
+ print("device connected")
2301
+ tsa.set_verbose(True) #detailed messages
2302
+ tsa.set_error_byte_return(True) #get explicit b'ERROR'
2303
+ msg = tsa.get_device_id()
2304
+ print(msg)
2305
+
2306
+
2307
+ tsa.disconnect()
2308
+ else:
2309
+ print("ERROR: could not connect to port")
2310
+