tiny_nearfieldscanner/emi_scanner.py

221 lines
6.8 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import time
import os
import sys
import serial
import csv
from tsapython import tinySA
import datetime
import numpy as np
import matplotlib.pyplot as plt
import progressbar
ser = serial.Serial("/dev/ttyACM0", 115200)
max_x = 250
max_y = 200
max_z = 150
min_z = 48
z_search = 100
prboffs_x = -10.0
prboffs_y = 25.0
prboffs_z = 0.0
feedrate = 5000
now = datetime.datetime.now()
def convert_data_to_arrays(start, stop, pts, data):
# using the start and stop frequencies, and the number of points,
freq_arr = np.linspace(
start, stop, pts
) # note that the decimals might go out to many places.
# you can truncate this because its only used
# for plotting in this example
# As of the Jan. 2024 build in some data returned with SWEEP or SCAN calls there is error data.
# https://groups.io/g/tinysa/topic/tinasa_ultra_sweep_command/104194367
# this shows up as "-:.000000e+01".
# TEMP fix - replace the colon character with a -10. This puts the 'filled in' points around the noise floor.
# more advanced filtering should be applied for actual analysis.
data1 = bytearray(data.replace(b"-:.0", b"-10.0"))
# get both values in each row returned (for reference)
# data_arr = [list(map(float, line.split())) for line in data.decode('utf-8').split('\n') if line.strip()]
# get first value in each returned row
data_arr = [
float(line.split()[0])
for line in data1.decode("utf-8").split("\n")
if line.strip()
]
return freq_arr, data_arr
def writeCSV(freq_array, data, csv_filename, args):
try:
with open(csv_filename, "w", newline="") as csvfile:
writer = csv.writer(csvfile, delimiter=";", quoting=csv.QUOTE_MINIMAL)
writer.writerow(
[f'# Nearfield Scan {now.strftime("%Y-%m-%d %H:%M")}, {args.desc}']
)
row = []
row.append("x")
row.append("y")
for elem in freq_array:
row.append(str(elem))
writer.writerow(row)
for key in data:
row = []
row.append(key[0])
row.append(key[1])
for i in range(len(data[key])):
row.append(data[key][i])
writer.writerow(row)
except IOError:
print("I/O error")
return
def initXYZ():
ser.write(str.encode("M17 \r\n"))
ser.write(str.encode("M84 X Y Z S12000\r\n"))
ser.write(str.encode("M84 E\r\n"))
ser.write(str.encode("G28\r\n"))
# set to absolute movement
ser.write(str.encode("G90\r\n"))
time.sleep(20)
def main():
import argparse
import configparser
parser = argparse.ArgumentParser(
prog="3D NearField Scanner",
description="Uses a 3D Printer and a Tiny SA for measureing the near field of a PCB",
epilog="=^.^=",
)
parser.add_argument("--fstart", type=float, default=1e6)
parser.add_argument("--fstop", type=float, default=800e6)
parser.add_argument("--pts", type=int, default=450)
parser.add_argument(
"--rbw",
help="RBW in kHz",
default="auto",
choices=["200", "1", "3", "10", "30", "100", "300", "600", "850"],
)
parser.add_argument("--xstart", type=int, default=0, help="x start in mm")
parser.add_argument("--xend", type=int, default=250, help="x end in mm")
parser.add_argument("--ystart", type=int, default=0, help="y start in mm")
parser.add_argument("--yend", type=int, default=200, help="y end in mm")
parser.add_argument("--xpts", type=int, default=10, help="number of x points")
parser.add_argument("--ypts", type=int, default=10, help="y number of y points")
parser.add_argument("--desc", type=str, default="", help="EUT Description")
parser.add_argument("--out", "-o", type=str, default="nearfieldscan", help="outfile Name")
parser.add_argument("--home", "-H", action="store_true", help="home printer")
args = parser.parse_args()
if args.home:
initXYZ()
progress=0
b = progressbar.ProgressBar(maxval=args.xpts*args.ypts)
b.start()
# scan surface
ser.write(str.encode(f"G1 Z{min_z}\r\n"))
input("Attach Probe and press Enter to continue...")
print("Starting Measurement")
# create a new tinySA object
tsa = tinySA()
# set the return message preferences
tsa.set_verbose(False) # detailed messages
tsa.set_error_byte_return(True) # get explicit b'ERROR' if error thrown
# attempt to autoconnect
found_bool, connected_bool = tsa.autoconnect()
# if port closed, then return error message
if connected_bool == False:
print("ERROR: could not connect to port")
else: # if port found and connected, then complete task(s) and disconnect
if args.rbw == "auto":
tsa.set_rbw_auto()
else:
tsa.rbw(int(args.rbw))
datadict = {}
ser.write(str.encode(f"G1 Z{z_search} F{feedrate}\r\n"))
try:
for y in np.linspace(args.ystart, args.yend, args.ypts,endpoint=True):
if y >= max_y:
break
else:
ser.write(str.encode(f"G1 X{args.xstart+prboffs_x} Y{y+prboffs_y} F{feedrate}\r\n"))
time.sleep(2)
for x in np.linspace(args.xstart, args.xend, args.xpts, endpoint=True):
if x >= max_x:
break
else:
ser.write(str.encode(f"G1 X{x+prboffs_x} Z{z_search} F{feedrate}\r\n"))
time.sleep(1)
ser.write(str.encode(f"G1 Z{min_z} F{feedrate}\r\n"))
ser.write(str.encode("M18 \r\n")) # disable steppers
time.sleep(2)
tsa.resume() # scan
data_bytes = tsa.scan(args.fstart, args.fstop, args.pts, 2)
tsa.wait
time.sleep(2)
ser.write(str.encode("M17 X Y Z\r\n")) # enable steppers
ser.write(str.encode(f"G1 Z{z_search} F{feedrate}\r\n"))
freq_arr, data_arr = convert_data_to_arrays(
args.fstart, args.fstop, args.pts, data_bytes
)
datadict[(x, y)] = data_arr
time.sleep(1)
progress+=1
b.update(progress)
except:
pass
finally:
b.finish()
writeCSV(
freq_arr,
datadict,
f'{now.strftime("%Y-%m-%d_%H%M")}_{args.out}.csv',
args,
)
tsa.resume() # resume so screen isn't still frozen
tsa.disconnect()
if __name__ == "__main__":
sys.exit(main())