Skip to content

Commit 307dad4

Browse files
committed
2 parents 728f808 + e591ab1 commit 307dad4

File tree

8 files changed

+309
-82
lines changed

8 files changed

+309
-82
lines changed

README.md

Lines changed: 155 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,13 @@ Done:
1111
* library commands for common args
1212
* documentation for original command usage and library functions
1313
* examples for basic functionality
14+
* some Debian-flavored Linux testing
1415

1516
Working on it:
1617
* filling in unfinished args and any new tinySA features
1718
* scan and scanraw occasionally throw back bad data. handled in an example, but actual fix in progress
1819
* trigger needs more alias funcs to cover full functionality
20+
* scanraw serial FROM DEVICE issues with the buffer. This is being explored see Example #3 in [Plotting Data with Matplotlib](#plotting-data-with-matplotlib) for details.
1921
* An argparse option + some example scripts
2022
* Beginner notes, vocab, and some examples for common usage
2123

@@ -84,16 +86,19 @@ numpy
8486
pandas
8587
matplotlib
8688
pillow
89+
pyQt5
8790

8891
```
8992

9093
For anyone unfamiliar with using requirements files, or having issues with the libraries, these can also be installed manually in the terminal (we recommend a Python virtual environment) with:
9194

9295
```python
93-
pip install pyserial numpy pandas matplotlib pillow
96+
pip install pyserial numpy pandas matplotlib pillow pyQt5
9497

9598
```
9699

100+
`pyQt5` is used with `matplotlib` to draw the figures. It needs to be installed in Linux systems to follow the examples included in tinySA_python, but is not needed on all Windows machines.
101+
97102

98103

99104
## Library Usage
@@ -120,7 +125,7 @@ Some error checking includes:
120125

121126
## Example Implementations
122127

123-
This library has been tested on Windows, but not yet on Unix systems. The primary difference should be the format of the serial port connection, but there may be smaller bugs in format that have not been detected yet.
128+
This library was developed on Windows and has been lightly tested on Linux. The main difference (so far) has been in the permissions for first access of the serial port, but there may be smaller bugs in format that have not been detected yet.
124129

125130
### Finding the Serial Port
126131

@@ -142,14 +147,18 @@ from src.tinySA_python import tinySA
142147
# create a new tinySA object
143148
tsa = tinySA()
144149

150+
# set the return message preferences
151+
tsa.set_verbose(True) #detailed messages
152+
tsa.set_error_byte_return(True) #get explicit b'ERROR' if error thrown
153+
154+
145155
# attempt to autoconnect
146156
found_bool, connected_bool = tsa.autoconnect()
147157

148158
# if port found and connected, then complete task(s) and disconnect
149-
if connected_bool == True: # or if success == True:
159+
if connected_bool == True:
150160
print("device connected")
151-
tsa.set_verbose(True) #detailed messages
152-
tsa.set_error_byte_return(True) #get explicit b'ERROR' if error thrown
161+
153162
msg = tsa.get_device_id()
154163
print(msg)
155164

@@ -195,9 +204,37 @@ Port: COM10, Description: USB Serial Device (COM10), Hardware ID: USB VID:PID=04
195204
#### Manually Finding a Port on Linux
196205

197206
```python
198-
TODO
207+
208+
import serial.tools.list_ports
209+
210+
ports = serial.tools.list_ports.comports()
211+
212+
for port, desc, hwid in ports:
213+
print(f"Port: {port}, Description: {desc}, Hardware ID: {hwid}")
214+
215+
```
216+
217+
```python
218+
219+
Port: /dev/ttyS0, Description: n/a, Hardware ID: n/a
220+
Port: /dev/ttyS3, Description: n/a, Hardware ID: n/a
221+
Port: /dev/ttyS2, Description: n/a, Hardware ID: n/a
222+
Port: /dev/ttyS1, Description: n/a, Hardware ID: n/a
223+
Port: /dev/ttyACM0, Description: tinySA4, Hardware ID: USB VID:PID=0483:5740 SER=400 LOCATION=3-3:1.0
224+
225+
```
226+
227+
This method identified the `/dev/ttyACM0`. Now, when attempting to use the autoconnect feature, the following error was initially returned:
228+
229+
```python
230+
[Errno 13] could not open port /dev/ttyACM0: [Errno 13] Permission denied: '/dev/ttyACM0'
231+
199232
```
200233

234+
This was due to not having permission to access the port. In this case, this error was solved by opening a terminal and executing `sudo chmod a+rw /dev/ttyACM0`. Should this issue be persistent, other solutions related to user groups and access will need to be investigated.
235+
236+
237+
201238

202239
### Serial Message Return Format
203240

@@ -227,6 +264,11 @@ from src.tinySA_python import tinySA
227264
# create a new tinySA object
228265
tsa = tinySA()
229266

267+
# set the return message preferences
268+
tsa.set_verbose(True) #detailed messages
269+
tsa.set_error_byte_return(True) #get explicit b'ERROR' if error thrown
270+
271+
230272
# attempt to connect to previously discovered serial port
231273
success = tsa.autoconnect()
232274

@@ -239,6 +281,7 @@ else:
239281
tsa.disconnect()
240282

241283
```
284+
242285
Example output for this method is as follows:
243286

244287
```python
@@ -313,16 +356,20 @@ from src.tinySA_python import tinySA
313356

314357
# create a new tinySA object
315358
tsa = tinySA()
359+
360+
# set the return message preferences
361+
tsa.set_verbose(True) #detailed messages
362+
tsa.set_error_byte_return(True) #get explicit b'ERROR' if error thrown
363+
364+
316365
# attempt to connect to previously discovered serial port
317366
success = tsa.autoconnect()
318367

319368
# if port open, then complete task(s) and disconnect
320369
if success == False:
321370
print("ERROR: could not connect to port")
322371
else:
323-
#detailed messages
324-
tsa.set_verbose(True) #detailed messages
325-
372+
326373
# get current trace data on screen
327374
msg = tsa.data(val=2)
328375
print(msg)
@@ -419,15 +466,21 @@ def convert_data_to_image(data_bytes, width, height):
419466

420467
# create a new tinySA object
421468
tsa = tinySA()
469+
470+
# set the return message preferences
471+
tsa.set_verbose(True) #detailed messages
472+
tsa.set_error_byte_return(True) #get explicit b'ERROR' if error thrown
473+
474+
422475
# attempt to connect to previously discovered serial port
423476
success = tsa.autoconnect()
424477

425478
# if port closed, then return error message
426479
if success == False:
427480
print("ERROR: could not connect to port")
428481
else: # port open, complete task(s) and disconnect
429-
# detailed messages turned on
430-
tsa.set_verbose(True)
482+
483+
431484
# get the trace data
432485
data_bytes = tsa.capture()
433486
print(data_bytes)
@@ -476,15 +529,20 @@ def byteArrayToNumArray(byteArr, enc="utf-8"):
476529

477530
# create a new tinySA object
478531
tsa = tinySA()
532+
533+
# set the return message preferences
534+
tsa.set_verbose(True) #detailed messages
535+
tsa.set_error_byte_return(True) #get explicit b'ERROR' if error thrown
536+
537+
479538
# attempt to connect to previously discovered serial port
480539
success = tsa.autoconnect()
481540

482541
# if port closed, then return error message
483542
if success == False:
484543
print("ERROR: could not connect to port")
485544
else: # port open, complete task(s) and disconnect
486-
# detailed messages turned on
487-
tsa.set_verbose(True)
545+
488546
# get the trace data
489547
data_bytes = tsa.data()
490548
print(data_bytes)
@@ -560,21 +618,26 @@ def convert_data_to_arrays(start, stop, pts, data):
560618

561619
# create a new tinySA object
562620
tsa = tinySA()
621+
622+
# set the return message preferences
623+
tsa.set_verbose(True) #detailed messages
624+
tsa.set_error_byte_return(True) #get explicit b'ERROR' if error thrown
625+
626+
563627
# attempt to autoconnect
564628
found_bool, connected_bool = tsa.autoconnect()
565629

566630
# if port closed, then return error message
567631
if connected_bool == False:
568632
print("ERROR: could not connect to port")
569633
else: # if port found and connected, then complete task(s) and disconnect
570-
# detailed messages turned on
571-
tsa.set_verbose(True)
572-
# set scan values
634+
573635
# set scan values
574636
start = int(1e9) # 1 GHz
575637
stop = int(3e9) # 3 GHz
576638
pts = 450 # sample points
577639
outmask = 2 # get measured data (y axis)
640+
578641
# scan
579642
data_bytes = tsa.scan(start, stop, pts, outmask)
580643

@@ -609,6 +672,16 @@ This example uses `scan()` and `scanraw()` to take a data measurement of data th
609672

610673
Extra processing needs to be done to get `dBm power` from `scanraw()`.
611674

675+
676+
NOTE FOR LINUX USERS: the serial read with SCANRAW is finicky. It's also ONLY with this function on Linux. Reading the serial buffer after SCANRAW failed in several situations:
677+
1. Requesting data too quickly after the last read
678+
* Expected, as the tinySA needs to resume and re-measure.
679+
2. Requesting data when the screen is frozen
680+
* Mildly expected, user error can trigger this too. Turns out in some situations, the frozen screen is not the same as a `pause`, and there is no data to flush from the buffer because no more data has been taken. This is either a safe error state, a feature of how SCANRAW works, or potentially a bug with the device/firmware/this library. Using the `resume()` function after this will restart measurements.
681+
3. {UNKNOWN}. There are several conditions that can cause issues, but it's unclear what 'symptoms' go to which problems
682+
* On the first few reads after the tinySA has been turned on and operational for at least 1 minute.
683+
* After sitting unused for more than a few minutes the returned buffer is < 50% the expected size or more than 5x the expected size. This is AFTER the flush command.
684+
612685

613686
```python
614687
# import tinySA library
@@ -642,20 +715,40 @@ def convert_data_to_arrays(start, stop, pts, data):
642715
# get first value in each returned row
643716
data_arr = [float(line.split()[0]) for line in data1.decode('utf-8').split('\n') if line.strip()]
644717

718+
# NOTE: if repeated read errors with utf-8 occur, uncomment the below as an alternative to the
719+
# line above. This will show you what value is being returned that caused the problem. It may
720+
# indicate a different problem with the serial connection permissions
721+
722+
# data_arr = []
723+
# for i, line in enumerate(data1.decode('utf-8').split('\n')):
724+
# print(f"Line {i}: '{line}'") # Show the raw line
725+
# line = line.strip()
726+
# if line:
727+
# try:
728+
# value = float(line.split()[0])
729+
# data_arr.append(value)
730+
# # print(f" Parsed float: {value}")
731+
# except ValueError as e:
732+
# print(f" Could not convert line to float: {line} — Error: {e}")
733+
645734
return freq_arr, data_arr
646735

647736

648737
# create a new tinySA object
649738
tsa = tinySA()
739+
740+
# set the return message preferences
741+
tsa.set_verbose(True) #detailed messages
742+
tsa.set_error_byte_return(True) #get explicit b'ERROR' if error thrown
743+
744+
650745
# attempt to autoconnect
651746
found_bool, connected_bool = tsa.autoconnect()
652747

653748
# if port closed, then return error message
654749
if connected_bool == False:
655750
print("ERROR: could not connect to port")
656751
else: # if port found and connected, then complete task(s) and disconnect
657-
# detailed messages turned on
658-
tsa.set_verbose(True)
659752

660753
# set scan values
661754
start = int(150e6) # 150 MHz
@@ -671,6 +764,12 @@ else: # if port found and connected, then complete task(s) and disconnect
671764
# SCAN RAW
672765
scanraw_data_bytes = tsa.scan_raw(start, stop, pts, outmask)
673766

767+
768+
# for subsequent reads, the tinySA does freeze while preforming SCANRAW
769+
# if there's an error, the screen will stay frozen (for reading).
770+
# So start it again so new data can be taken
771+
tsa.resume()
772+
674773
# disconnect because we don't need the tinySA to process data
675774
tsa.disconnect()
676775

@@ -686,24 +785,35 @@ else: # if port found and connected, then complete task(s) and disconnect
686785
# 'xH'*pts: a repetition of the format 'xH' once per point.
687786
# 'x': represents a pad byte, which is ignored
688787
# 'H': represents an unsigned short integer (2 bytes)
689-
processed_scanraw = struct.unpack( '<' + 'xH'*pts, bin_scanraw ) # ignore trailing '}ch> '
690-
processed_scanraw = np.array(processed_scanraw, dtype=np.uint16 ).reshape(-1, 1)
691-
692-
# CONVERT to dBm Power
693-
# take the processed binary data and convert it to dBm.
694-
# The equation is from tinySA.org & official documentation
695-
SCALE_FACTOR = 174 # tinySA Basic: 128, tinySA Ultra and newer is 174
696-
dBm_data = processed_scanraw / 32 - SCALE_FACTOR
697-
print(dBm_data)
698-
699-
# plot
700-
plt.plot(freq_arr, data_arr, label= 'SCAN data')
701-
plt.plot(freq_arr, dBm_data, label= 'SCANRAW data')
702-
plt.xlabel("frequency (hz)")
703-
plt.ylabel("measured data (dBm)")
704-
plt.title("tinySA SCAN and SCANRAW data")
705-
plt.legend()
706-
plt.show()
788+
789+
expected_len = 3 * pts
790+
actual_len = len(bin_scanraw)
791+
print(f"Expected length: {expected_len}, Actual length: {actual_len}")
792+
793+
if actual_len == expected_len:
794+
# SCANRAW has returned the expected amount of data for the read.
795+
# sometimes this function (and not SCAN) does not read the buffer properly
796+
# a fix is in progress for LINUX systems. it works fine for Windows
797+
processed_scanraw = struct.unpack( '<' + 'xH'*pts, bin_scanraw ) # ignore trailing '}ch> '
798+
processed_scanraw = np.array(processed_scanraw, dtype=np.uint16 ).reshape(-1, 1) #unit8 has overflow error
799+
800+
# CONVERT to dBm Power
801+
# take the processed binary data and convert it to dBm.
802+
# The equation is from tinySA.org & official documentation
803+
SCALE_FACTOR = 174 # tinySA Basic: 128, tinySA Ultra and newer is 174
804+
dBm_data = processed_scanraw / 32 - SCALE_FACTOR
805+
print(dBm_data)
806+
807+
# plot
808+
plt.plot(freq_arr, data_arr, label= 'SCAN data')
809+
plt.plot(freq_arr, dBm_data, label= 'SCANRAW data')
810+
plt.xlabel("frequency (hz)")
811+
plt.ylabel("measured data (dBm)")
812+
plt.title("tinySA SCAN and SCANRAW data")
813+
plt.legend()
814+
plt.show()
815+
else:
816+
print("SCANRAW did not return the expected amount of data for the read")
707817

708818
```
709819
<p align="center">
@@ -725,20 +835,26 @@ from src.tinySA_python import tinySA
725835

726836
# create a new tinySA object
727837
tsa = tinySA()
838+
839+
# set the return message preferences
840+
tsa.set_verbose(True) #detailed messages
841+
tsa.set_error_byte_return(True) #get explicit b'ERROR' if error thrown
842+
843+
728844
# attempt to autoconnect
729845
found_bool, connected_bool = tsa.autoconnect()
730846

731847
# if port closed, then return error message
732848
if connected_bool == False:
733849
print("ERROR: could not connect to port")
734850
else: # if port found and connected, then complete task(s) and disconnect
735-
# detailed messages turned on
736-
tsa.set_verbose(True)
851+
737852
# set scan values
738853
start = 150e6 # 150 MHz
739854
stop = 200e6 # 200 MHz
740855
pts = 450 # for tinySA Ultra
741856
outmask = 1 # get measured data (y axis)
857+
742858
# scan
743859
data_bytes = tsa.command("scan 150e6 200e6 5 2")
744860

@@ -1437,7 +1553,7 @@ Marker levels will use the selected unit Marker peak will activate the marker (i
14371553
### **scanraw**
14381554
* **Description:** Performs a scan of unlimited amount of points and sends the data in binary form
14391555
* **Original Usage:** `scanraw {start(Hz)} {stop(Hz)} [points][option]` or `scanraw {start(Hz)} {stop(Hz)} [points] [unbuffered]` depending on the source
1440-
* **Direct Library Function Call:**
1556+
* **Direct Library Function Call:** `scan_raw(start=Int|Float, stop=Int|Float, pts=Int|Float, unbuf=1)`
14411557
* **Example Return:**
14421558
* Raw, unprocessed return for 15 pts: `b'scanraw 150000000 200000000 15 2\r\n{x"\nx3\nx4\nx\x15\nx6\nx\x07\nx)\nxj\nx\xfb\txm\nx]\nxO\nxp\nx\xb2\x0bx3\x0c}ch>'`
14431559
* Example arg: `scanraw 150e6 200e6 5 1`

0 commit comments

Comments
 (0)