############################################################################### import argparse import threading import time import serial import copy import binascii import matplotlib.pyplot as plt import math ############################################################################### parser = argparse.ArgumentParser(description='SWR meter helper tool.') parser.add_argument("-d", "--device", type=str, help="The control device like /dev/ttyUSB0 or COM3.") # start_freq parser.add_argument("-s", "--start_freq", type=int, help="") # end_freq parser.add_argument("-e", "--end_freq", type=int, help="") # intervall parser.add_argument("-i", "--intervall", type=int, help="") # step_freq parser.add_argument("-p", "--step_freq", type=int, help="") # drive_str parser.add_argument("-a", "--drive_str", type=int, help="") # start measurement parser.add_argument("-m", "--start_meas", default=False, help="", action='store_true') # output file (CSV) parser.add_argument("-o", "--output_file", type=str, help="") # show graphical results parser.add_argument("-g", "--show_graph", default=False, help="", action='store_true') # get config #parser.add_argument("-c", "--get_config", default=False, help="", action='store_true') ############################################################################### MSG_SOD1 = 0x3c MSG_SOD2 = 0x3e MSG_EOD1 = 0x0d MSG_EOD2 = 0x0a MSG_TYPE_ANSWER_OK = 0x01 MSG_TYPE_ANSWER_NOK = 0x02 MSG_TYPE_MEAS_FREQ_INFO = 0x03 MSG_TYPE_MEAS_END_INFO = 0x04 MSG_TYPE_CONFIG = 0x05 MSG_TYPE_ANSWER_OK_DATA_TO_RECV = 0 MSG_TYPE_ANSWER_NOK_DATA_TO_RECV = 0 MSG_TYPE_MEAS_FREQ_INFO_DATA_TO_RECV = 8 MSG_TYPE_MEAS_END_INFO_DATA_TO_RECV = 0 MSG_TYPE_CONFIG_DATA_TO_RECV = 15 CC_CMD_SET_START_FREQ = 0x01 CC_CMD_SET_END_FREQ = 0x02 CC_CMD_SET_INTERVALL = 0x03 CC_CMD_SET_DRIVE_STRENGTH = 0x04 CC_CMD_SET_FREQ_STEP = 0x05 CC_CMD_START_MEASUREMENT = 0x06 CC_CMD_GET_CONFIG = 0x10 ############################################################################### TIMEOUT_CNT_MAX = 150 MAIN_LOOP_DELAY_S = 0.05 THREAD_LOOP_DELAY_S = 0.01 ############################################################################### ser = None device = "/dev/ttyUSB0" ############################################################################### CC_STATE_WAIT_SOD1 = 0x01 CC_STATE_WAIT_SOD2 = 0x02 CC_STATE_READ_DATA = 0x03 CC_STATE_WAIT_EOD1 = 0x04 CC_STATE_WAIT_EOD2 = 0x05 CC_STATE_GET_TYPE = 0x06 cc_state = CC_STATE_WAIT_SOD1 cc_state_list = [ CC_STATE_WAIT_SOD1, CC_STATE_WAIT_SOD2, CC_STATE_READ_DATA, CC_STATE_WAIT_EOD1, CC_STATE_WAIT_EOD2 ] cc_state_fn = {} msg_type_list = [ MSG_TYPE_ANSWER_OK, MSG_TYPE_ANSWER_NOK, MSG_TYPE_MEAS_FREQ_INFO, MSG_TYPE_CONFIG, MSG_TYPE_MEAS_END_INFO, ] msg_type_data_to_read = { MSG_TYPE_ANSWER_OK : MSG_TYPE_ANSWER_OK_DATA_TO_RECV, MSG_TYPE_ANSWER_NOK : MSG_TYPE_ANSWER_NOK_DATA_TO_RECV, MSG_TYPE_MEAS_FREQ_INFO : MSG_TYPE_MEAS_FREQ_INFO_DATA_TO_RECV, MSG_TYPE_CONFIG : MSG_TYPE_CONFIG_DATA_TO_RECV, MSG_TYPE_MEAS_END_INFO : MSG_TYPE_MEAS_END_INFO_DATA_TO_RECV, } msg_type = 0 cc_data_read = 0 cc_data_buffer = [] # yes a separate counter to manage the order of the received messages cc_message_cnt = 0 cc_received_messages = [] ############################################################################### thread_obj = None thread_lock = None thread_started = False thread_stop = False ############################################################################### def cc_init(): global cc_state_fn global cc_state cc_state = CC_STATE_WAIT_SOD1 cc_state_fn = { CC_STATE_WAIT_SOD1 : cc_state_fn_wait_for_sod1, CC_STATE_WAIT_SOD2 : cc_state_fn_wait_for_sod2, CC_STATE_WAIT_EOD1 : cc_state_fn_wait_for_eod1, CC_STATE_WAIT_EOD2 : cc_state_fn_wait_for_eod2, CC_STATE_GET_TYPE : cc_state_fn_get_type, CC_STATE_READ_DATA : cc_state_fn_read_data, } ########## function to call by the thread def cc_dataReceiverThread(): global ser global cc_state global cc_state_fn global thread_started global thread_stop thread_started = True while thread_stop == False: # 1. read byte from serial port into incoming incoming = [] bytesToRead = ser.inWaiting() if bytesToRead > 0: incoming = list(ser.read(64)) # 2. process the received data for c in incoming: c = int(binascii.hexlify(c), 16) # call the cc_state specific function to process the currently received byte cc_state_fn[cc_state](c) if cc_state not in cc_state_list: cc_state = CC_STATE_WAIT_SOD1 time.sleep(THREAD_LOOP_DELAY_S) thread_started = False ########## def cc_startReceiverThread(): global thread_obj global thread_lock global thread_stop if thread_started == False: thread_lock = threading.Lock() thread_obj = threading.Thread(target=cc_dataReceiverThread) thread_obj.start() thread_stop = False ########## def cc_stopReceiverThread(): global thread_obj global thread_started global thread_stop if thread_started == True: thread_stop = True thread_obj.join() # wait for the thread to finish ##### CC_STATE_WAIT_SOD1 def cc_state_fn_wait_for_sod1(c): global cc_data_read global msg_type global cc_data_buffer global cc_state cc_data_read = 0 msg_type = 0 cc_data_buffer = [] if c == MSG_SOD1: cc_state = CC_STATE_WAIT_SOD2 else: cc_state = CC_STATE_WAIT_SOD1 ##### CC_STATE_WAIT_SOD2 def cc_state_fn_wait_for_sod2(c): global cc_state if c == MSG_SOD2: cc_state = CC_STATE_GET_TYPE else: cc_state = CC_STATE_WAIT_SOD1 ##### CC_STATE_GET_TYPE def cc_state_fn_get_type(c): global msg_type global cc_state if c in msg_type_list: msg_type = c if msg_type_data_to_read[msg_type] > 0: cc_state = CC_STATE_READ_DATA else: cc_state = CC_STATE_WAIT_EOD1 else: cc_state = CC_STATE_WAIT_SOD1 ##### CC_STATE_READ_DATA def cc_state_fn_read_data(c): global cc_data_buffer global cc_data_read global cc_state if cc_data_read <= msg_type_data_to_read[msg_type] - 1: cc_data_buffer.append(c) cc_data_read = cc_data_read + 1 if cc_data_read == msg_type_data_to_read[msg_type]: cc_state = CC_STATE_WAIT_EOD1 ##### CC_STATE_WAIT_EOD1 def cc_state_fn_wait_for_eod1(c): global cc_state if c == MSG_EOD1: cc_state = CC_STATE_WAIT_EOD2 else: cc_state = CC_STATE_WAIT_SOD1 ##### CC_STATE_WAIT_EOD2 def cc_state_fn_wait_for_eod2(c): global thread_lock global cc_message_cnt global cc_state if c == MSG_EOD2: is_message_read = False thread_lock.acquire() cc_received_messages.append([ cc_message_cnt, msg_type, is_message_read, copy.deepcopy(cc_data_buffer) ]) thread_lock.release() cc_message_cnt = cc_message_cnt + 1 cc_state = CC_STATE_WAIT_SOD1 ############################################################################### ##### def openSerialDevice(d): global ser try: ser = serial.Serial(d) except: print "ERROR: Can't open the serial device " + device exit(1) # Toggle DTR to reset Arduino ser.setDTR(False) time.sleep(1) # toss any data already received, see # http://pyserial.sourceforge.net/pyserial_api.html#serial.Serial.flushInput ser.flushInput() ser.setDTR(True) try: ser = serial.Serial( port=d,\ baudrate=115200,\ parity=serial.PARITY_NONE,\ stopbits=serial.STOPBITS_ONE,\ bytesize=serial.EIGHTBITS,\ rtscts=0,\ timeout=0) except: print "ERROR: Can't open the serial device " + device exit(2) ##### def closeSerialDevice(): global ser ser.flush() ser.read(1000) ser.close() ##### def sendSerialData(data): global ser ser.write(bytearray([ MSG_SOD1, MSG_SOD2 ])) ser.write(bytearray(data)) ser.write(bytearray([ MSG_EOD1, MSG_EOD2 ])) ser.flush() ############################################################################### def user_friendly_freq(f): if f >= 1000000: return str(f / 1000000.0) + " MHz" elif f >= 1000: return str(f / 1000.0) + " kHz" return str(f) + " Hz" ############################################################################### if __name__ == "__main__": start_freq = 0 end_freq = 0 step_freq = 0 config_read = False meas_data = [] cc_init() # parse the commandline arguments args = parser.parse_args() dataSend = 0 timeout = 0 # 1. open serial device or abort if args.device != None: device = args.device openSerialDevice(device) # 2. start thread to poll cc_dataReceiverThread() cc_startReceiverThread() time.sleep(1.5) # 3. get and process the commandline arguments/parameter #if args.get_config == True and args.start_meas == False: # print "sent: GET_CONFIG" # sendSerialData([CC_CMD_GET_CONFIG]) # dataSend = dataSend + 1 if args.start_freq != None: print "sent: SET_START_FREQ" sendSerialData([CC_CMD_SET_START_FREQ, (args.start_freq & 0xff000000) >> 24, (args.start_freq & 0x00ff0000) >> 16, (args.start_freq & 0x0000ff00) >> 8, (args.start_freq & 0x000000ff)]) dataSend = dataSend + 1 if args.end_freq != None: print "sent: SET_END_FREQ" sendSerialData([CC_CMD_SET_END_FREQ, (args.end_freq & 0xff000000) >> 24, (args.end_freq & 0x00ff0000) >> 16, (args.end_freq & 0x0000ff00) >> 8, (args.end_freq & 0x000000ff)]) dataSend = dataSend + 1 if args.step_freq != None: print "sent: SET_FREQ_STEP" sendSerialData([CC_CMD_SET_FREQ_STEP, (args.step_freq & 0xff000000) >> 24, (args.step_freq & 0x00ff0000) >> 16, (args.step_freq & 0x0000ff00) >> 8, (args.step_freq & 0x000000ff)]) dataSend = dataSend + 1 if args.intervall != None: print "sent: SET_INTERVALL" sendSerialData([CC_CMD_SET_INTERVALL, (args.intervall & 0x0000ff00) >> 8, (args.intervall & 0x000000ff)]) dataSend = dataSend + 1 if args.drive_str != None: print "sent: SET_DRIVE_STRENGTH" sendSerialData([CC_CMD_SET_DRIVE_STRENGTH, args.drive_str]) dataSend = dataSend + 1 if args.start_meas == True: print "sent: START_MEASUREMENT" sendSerialData([CC_CMD_START_MEASUREMENT]) dataSend = dataSend + 1 # 4. start main loop while dataSend > 0 and timeout < TIMEOUT_CNT_MAX: thread_lock.acquire() tmp_messages = copy.deepcopy(cc_received_messages) thread_lock.release() # 4.1 test for the response(s) for e in tmp_messages: if e[2] == False: # test for unread message # process it and set the data to read if e[1] == MSG_TYPE_ANSWER_OK: print "recv: OK" elif e[1] == MSG_TYPE_ANSWER_NOK: print "recv: NOT OK" elif e[1] == MSG_TYPE_MEAS_FREQ_INFO: print "recv: FREQ INFO" freq = e[3][0] << 24 freq += e[3][1] << 16 freq += e[3][2] << 8 freq += e[3][3] a0 = e[3][4] << 8 a0 += e[3][5] a1 = e[3][6] << 8 a1 += e[3][7] print "freq: " + user_friendly_freq(freq) print "a0: " + str(a0) print "a1: " + str(a1) meas_data.append([ freq, a0, a1 ]) elif e[1] == MSG_TYPE_CONFIG: print "recv: CONFIG" start_freq = e[3][0] << 24 start_freq += e[3][1] << 16 start_freq += e[3][2] << 8 start_freq += e[3][3] end_freq = e[3][4] << 24 end_freq += e[3][5] << 16 end_freq += e[3][6] << 8 end_freq += e[3][7] step_freq = e[3][8] << 24 step_freq += e[3][9] << 16 step_freq += e[3][10] << 8 step_freq += e[3][11] intervall = e[3][12] << 8 intervall += e[3][13] drive_str = e[3][14] print "start_freq = " + user_friendly_freq(start_freq) print "end_freq = " + user_friendly_freq(end_freq) print "step_freq = " + user_friendly_freq(step_freq) print "intervall = " + str(intervall) + " ms" print "drive_str = " + str((drive_str + 1) * 2) + " mA" if args.start_meas == True and config_read == False: dataSend = dataSend + 1 + ((end_freq - start_freq) / step_freq) config_read = True elif e[1] == MSG_TYPE_MEAS_END_INFO: print "recv: END INFO" meas_freq = [] meas_ratio = [] meas_r = [] meas_p = [] min_vswr = [ 10, 0 ] # the default VSWR is 10 and the default freq is 0] i = ((drive_str + 1) * 2.0) / 1000.0 if args.output_file != None or args.show_graph == True: for m in meas_data: meas_freq.append(m[0]) vswr = 0 if m[1] > 0 and m[2] > 0: if m[1] > m[2]: vswr = (m[1] / m[2]) meas_ratio.append(vswr) elif m[1] < m[2]: vswr = (m[2] / m[1]) meas_ratio.append(vswr) else: vswr = 1 meas_ratio.append(1) else: vswr = 1 meas_ratio.append(1) if vswr < min_vswr[0]: min_vswr[0] = vswr min_vswr[1] = m[0] # the frequency # R = meas_data[0] * 5V / 1023 * I r = (m[1] * 5.0 / 1023.0) / i meas_r.append(r) # P = meas_data[0] * 5V ^ 2 / r if r > 0: meas_p.append(math.pow(i, 2) * r) else: meas_p.append(0) if args.output_file != None: FILE = open(args.output_file, "w") FILE.write("freqency;ratio;impedance;watt;drive;a0;a1\n") j = 0 for m in meas_ratio: FILE.write("%f;%f;%f;%f;%f;%d,%d\n" % (meas_freq[j], m, meas_r[j], meas_p[j], i, meas_data[j][1], meas_data[j][2])) j = j + 1 FILE.close() print "Output file " + args.output_file + " written." if args.show_graph == True: plt.plot(meas_freq, meas_ratio) plt.plot(meas_freq, meas_r) plt.plot(meas_freq, meas_p) print "Please close the mathplot window to exit..." plt.show() print "First minimum VSWR " + str(min_vswr[0]) + " found at freqency " + user_friendly_freq(min_vswr[1]) else: print "err: unknown type 0x%02x" % (e[1]) break thread_lock.acquire() cc_received_messages[e[0]][2] = True thread_lock.release() timeout = 0 # reset the timeout # reduce the number of messages to receive dataSend = dataSend - 1 # manage the timeout behaviour time.sleep(MAIN_LOOP_DELAY_S) timeout = timeout + 1 if timeout >= TIMEOUT_CNT_MAX: print "Timeout happened" # 5. stop data processing thread cc_stopReceiverThread() # 6. close serial device closeSerialDevice() exit(0)