Wafer Zero Scan SmartRemote Code
Python
Wafer Zero Scan code used in the Quality Assurance Automation Tool
main
1 file
Wafer Zero Scan …..py
wafer_zero_scan.py
8.1 KB
Wafer Zero Scan …..py
8154 bytes
from src.core.afm import AFM as AFM
from src.core.afm import CHECKER as CHECKER
import src.core.analysis
import src.core.report
import src.core.tiff
from time import sleep
import numpy as np
import os
class Wafer_Zero_Scan():
def __init__(self):
self.afm = AFM()
self.base_dir = ''
self.sub_dir = 'Wafer Zero Scan'
self.file_name = 'RAW'
self.parameter = None
def move_to(self):
pass
def set(self):
if self.parameter['xy_servo_enable'] == 'On':
self.sub_dir += '/XYservo_On'
self.parameter['z_scanner_range'] = 70
self.parameter['xy_scanner_range'] = 100
self.afm.enable_xy_servo()
elif self.parameter['xy_servo_enable'] == 'Off':
self.sub_dir += '/XYservo_Off'
self.log_name = 'Position_XY.csv'
self.parameter['z_scanner_range'] = 20
self.parameter['xy_scanner_range'] = 20
self.afm.enable_xy_servo('off')
self.header = 'Repeat, Lable , Input X Pos., Input Y Pos., Output X Pos., Output Y Pos.\n '
if self.parameter['lift_enable'] == 'On':
self.sub_dir += '_Lift_On'
elif self.parameter['lift_enable'] == 'Off':
self.sub_dir += '_Lift_Off'
dir_path = self.base_dir + '\\' + self.sub_dir
self.log_path = dir_path + '\\' + self.log_name
if not os.path.exists(dir_path):os.makedirs(dir_path)
fd = open(self.log_path,'w')
fd.write(self.header)
fd.close()
self.afm.set_data_location(self.base_dir, self.sub_dir, self.file_name)
self.afm.set_head_mode('contact')
self.afm.clear_channels()
self.afm.add_channel('ChannelZDriveOrTopography')
self.afm.stop_scan()
self.afm.set_z_scanner_range(self.parameter['z_scanner_range'])
self.afm.set_xy_scanner_range(self.parameter['xy_scanner_range'])
self.bck_scan_geometry = self.afm.get_scan_geometry_all()
self.bck_scan_option = self.afm.get_scan_option_all()
self.bck_approach_option = self.afm.get_approach_option_all()
self.afm.set_scan_geometry(self.parameter['pixels'][0],self.parameter['pixels'][1], 0, 0)
self.afm.set_scan_option(sine_scan=False,over_scan=False, \
over_scan_percent=0, tow_way=False, \
det_driven=False, force_slope_correction=False, \
interlace=False,slow_scan='off')
self.afm.set_scan_rate(self.parameter['scan_rate'])
self.afm.enable_z_servo()
self.afm.set_z_servo(self.parameter['gain'], self.parameter['setpoint'])
self.afm.set_approach_option()
def unset(self):
self.afm.set_scan_option_dict(self.bck_scan_option)
self.afm.set_scan_geometry_dict(self.bck_scan_geometry)
self.afm.set_scan_option_dict(self.bck_scan_option)
def __approach(self):
self.afm.start_approach('q+s')
wait_time = self.parameter['post_approach_wait']
print(f"Waiting for {wait_time} seconds")
sleep(wait_time)
def __run(self):
self.afm.trigger_image_scan()
isscan = True
while isscan:
test = self.afm.query_scan_status()
if test == 'true':isscan=True;
else:isscan=False
sleep(2)
def approach(self):
pass
def run(self):
pos = np.array(self.parameter['target_positions']) # 'target_positions'에서 좌표 데이터 로드
pos_int = [] # 문자열 배열을 정수형 배열로 변환(빈 문자열은 무시됨)
for row in pos:
if row[0] and row[1]: # 문자열이 비어 있지 않으면
try:
pos_int.append([int(row[0]), int(row[1])])
except ValueError:
print(f"Error converting position to integer: {row}")
continue
pos_int = np.array(pos_int)
norm_speed_x = self.parameter['norm_speed']
norm_speed_y = self.parameter['norm_speed']
for i, (x, y) in enumerate(pos_int): # 반복문을 통해 좌표 이동 후 Zero Scan 수행
self.afm.moveto_xy_stage(x, y, norm_speed_x, norm_speed_y)
reply = self.afm.query_stage_pos()
sw_x = reply['x']
sw_y = reply['y']
sleep(2)
# 로그 기록
line = f'{self.iter}, P{i}, {x}, {y}, {sw_x}, {sw_y}\n'
self.file_name = f'RAW_Repeat_{self.iter}_P{i}'
self.afm.set_data_location(self.base_dir, self.sub_dir, self.file_name)
with open(self.log_path, "a") as log_fd:
log_fd.write(line)
self.__approach()
self.__run()
self.afm.lift_z_stage(1000)
def done(self):
self.afm.stop_scan()
self.afm.stop_scan()
if self.parameter['lift_enable'] == 'On':
self.afm.lift_z_stage(1000)
def stop(self):
checker = CHECKER()
checker.abort_approach()
checker.abort_scan()
def analyze(self):
find_dir = self.base_dir + '\\' + self.sub_dir
tiff_list = src.core.report.ls_tiff(find_dir)
read_tiff = src.core.tiff.Read_tiff()
write_tiff = src.core.tiff.Write_tiff()
result_mean = []
result_min = []
result_max = []
result_name = []
result_rq = []
for tiff_path in tiff_list:
tiff_name = tiff_path.split('\\')[-1]
tmp_name = tiff_name.split('.')[0]
condition_1 = tmp_name.split('_')[-3] == 'Z Height'
condition_2 = tmp_name.split('_')[-2] == 'Forward'
tmp_name_list = tiff_name.split('_')
tmp_name = ''
for s in tmp_name_list[1:]: tmp_name += '_' + s
tmp_name = tmp_name.split('.')[0]
tmp_name = tmp_name[1:]
if condition_1 & condition_2:
dict_tiff , ori_tiff = read_tiff(tiff_path)
image = dict_tiff['IMAGE']
flatten_image = src.core.analysis.one_d_flatten(image)
dict_tiff['IMAGE'] = flatten_image
cmap = dict_tiff['COLORMAP']
save_path = self.base_dir + '\\' +self.sub_dir + '\\Flatten\\' + 'Flatten_' + tmp_name + '.tiff'
report_dir = self.base_dir + '\\' +self.sub_dir + '\\Report\\'
write_tiff(ori_tiff,dict_tiff,save_path)
# convert scale : um ->nm
scale_factor = 1E+3
tmp_mean, tmp_min, tmp_max = src.core.analysis.cal_statistic(flatten_image)
tmp_mean *= scale_factor; tmp_min *= scale_factor; tmp_max *= scale_factor
tmp_rq = src.core.analysis.cal_r_q(flatten_image) * scale_factor
tmp_data = [
['Mean', 'nm',f'{tmp_mean:.3f}'],
['Min', 'nm', f'{tmp_min:.3f}'],
['Max', 'nm', f'{tmp_max:.3f}'],
['Rq', 'nm', f'{tmp_rq:.3f}']
]
flatten_image *= scale_factor
src.core.report.default_report(tmp_name, report_dir, dict_tiff, cmap, tmp_data)
result_name.append(tmp_name)
result_mean.append(tmp_mean)
result_min.append(tmp_min)
result_max.append(tmp_max)
result_rq.append(tmp_rq)
csv_path = self.base_dir + '\\' + self.sub_dir + "\\result.csv"
result_dict = {'File name': result_name,
'Mean (nm)': result_mean,
'Min (nm)' : result_min,
'Max (nm)' : result_max,
'Rq (nm)' : result_rq
}
src.core.report.write_csv(result_dict,csv_path,['Mean (nm)','Min (nm)','Max (nm)','Rq (nm)'])
Comments (0)
No comments yet. Be the first to comment!
Snippet Information
Author:
taehun.yang
Language: Python
Created:
Oct 23, 2025
Updated:
0 minutes ago
Views: 47
Stars: 2