def __get_user_rate(self, type, port): old_completer = readline.get_completer() rates = [] for rate in Connection.BAUDRATES: rates.append(str(rate)) compl_rates = Completer(rates) readline.set_completer(compl_rates.list_completer) rate = None while rate is None: rate = input(f'{Fore.YELLOW}{type} rate:{Fore.RESET} ').strip() if rate in ['q', 'exit']: break elif rate == '': rate = Connection.get_baudrate(port) elif rate not in rates: error(f'Rate {rate} is not valid.') warning('Valid baudrates:') for rate in rates: warning('\t' + rate) warning('Type \'exit\' to return.') rate = None else: rate = int(rate) readline.set_completer(old_completer) return rate
def get_ack(self, timeout): length = b'' while length == b'': length = self.connection.read(2) time.sleep(0.01) self.connection.read(1) # Checksum self.connection.read(1) # 0x00 response = self.connection.read(1) while response not in [OPCODE.ACK, OPCODE.NACK]: if self.start_time == 0: self.start_time = time.perf_counter() if time.perf_counter() - self.start_time > timeout: response = None break response = self.connection.read(1) time.sleep(0.01) self.start_time = 0 if response == OPCODE.ACK: return True elif response == OPCODE.NACK: error('Received NACK') return False else: error('Received unexpected data or timeout!') return False
def do_listen(self, args=''): ''' Start listener and parser thread Starting listener on connected ports. If mmWave is not configured, default configuration will be send first. Look \'connect\' and \'autoconnect\' command for connecting to mmWave. Usage: >> listen ''' if args != '': error('Unknown arguments.') return if not self.configured: warning('mmWave not configured.') return with self.listening_lock: if self.listening: warning('Listener already started.') return with self.listening_lock: self.listening = True self.__listen_thread() self.__parse_thread()
def __get_user_port(self, type): old_completer = readline.get_completer() ports = [] for port in serial.tools.list_ports.comports(): ports.append(port[0]) compl_ports = Completer(ports) readline.set_completer_delims('\t') readline.parse_and_bind('tab: complete') readline.set_completer(compl_ports.list_completer) port = None while port is None: port = input(f'{Fore.YELLOW}{type} port:{Fore.RESET} ').strip() if port in ['q', 'exit']: return elif port not in ports: error(f'Port {port} is not valid.') warning('Valid ports:') for port in ports: warning('\t' + port) warning('Type \'exit\' to return.') port = None readline.set_completer(old_completer) return port
def do_train(self, args=''): ''' Train neural network Command will first load cached X and y data located in 'mmwave/data/.X_data' and 'mmwave/data/.y_data' files. This data will be used for the training process. If you want to read raw .csv files, provide \'refresh\' (this will take few minutes). Usage: >> train >> train refresh ''' if len(args.split()) > 1: error('Unknown arguments.') return if args == '': refresh_data = False elif args == 'refresh': refresh_data = True else: warning(f'Unknown argument: {args}') return X, y = Logger.get_all_data(refresh_data=refresh_data) Logger.get_stats(X, y) X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=.3, stratify=y, random_state=12) self.model.train(X_train, y_train, X_val, y_val)
def do_print(self, args=''): ''' Pretty print Printing received frames. Listener should be started before using this command. Use <Ctrl-C> to stop this command. Usage: >> print ''' if args != '': error('Unknown arguments.') return with self.listening_lock: if not self.listening: error('Listener not started.') return with self.printing_lock: self.printing = True self.console_queue.get() with self.printing_lock: self.printing = False
def do_predict(self, args=''): ''' Start prediction Passing received frames through neural network and printing results. Listener should be started before using this command. Use <Ctrl-C> to stop this command. Usage: >> predict ''' if args != '': error('Unknown arguments.') return with self.listening_lock: if not self.listening: error('Listener not started.') return if not self.__model_loaded(): self.model.load() print() with self.predicting_lock: self.predicting = True self.__predict_thread() self.console_queue.get() with self.predicting_lock: self.predicting = False self.model_queue.put(None)
def do_remove(self, args=''): ''' Remove last sample Removing last gesture sample. Data will be removed from \'mmwave/data/gesture_foder\' folder. Possible options: \'up\', \'down\', \'left\', \'right\', \'cw\', \'ccw\', \'s\', \'z\', \'x\' Usage: >> remove up >> remove ccw >> remove z ''' if args == '': error('too few arguments.') return if len(args.split()) > 1: error('Unknown arguments.') return if not GESTURE.check(args): warning(f'Unknown argument: {args}') return self.logger.set_gesture(args) self.logger.discard_last_sample()
def default(self, line): ''' Called on an input line when the command prefix is not recognized. In that case we execute the line as Python code. ''' try: exec(line) in self._locals, self._globals except Exception: error('Unknown arguments.') return
def do_send(self, args=''): ''' Sending configuration to mmWave Configuring mmWave with given configuration file. All configuration files should be placed in \'mmwave/communication/profiles\' folder. Use <Tab> for autocompletion on available configuration files. All configuration files should have .cfg extension. If no configuration is provided, default configuration file will be used. Usage: >> send >> send profile ''' if args == '': args = self.default_config if len(args.split()) > 1: error('Too many arguments.') return if not self.__is_connected(): error('Not connected.') return cfg = os.path.join(self.config_dir, args + '.cfg') if cfg not in glob.glob(os.path.join(self.config_dir, '*.cfg')): print('Unknown profile.') return mmwave_configured = False cnt = 0 while not mmwave_configured and cnt < 5: mmwave_configured = self.mmwave.configure(cfg) if not mmwave_configured: signal_cnt = 0 while signal_cnt < 5: if not self.console_queue.empty(): self.console_queue.get() return time.sleep(.2) signal_cnt += 1 cnt += 1 if not mmwave_configured: return if os.path.basename(cfg) == 'stop.cfg': self.configured = False else: self.configured = True self.config = cfg
def do_exit(self, args): '''Exits from the console''' if args != '': error('Unknown arguments.') return self.do_stop() if self.__is_connected(): self.mmwave.disconnect() os._exit(0)
def send_cmd(self, command, get_status=False, resp=True): self.send_packet(command) if not self.get_ack(command.timeout): error('Command was not successful!') return if get_status: self.send_packet(CMD(OPCODE.GET_STATUS)) response = '' if resp: response = self.get_response() return response
def do_get_model(self, args=''): ''' Get current model type. Usage: >> get_model ''' if args != '': error('Unknown arguments.') return print(f'Current model type: {self.model_type}')
def do_eval(self, args=''): ''' Evaluate neural network Command will first load cached X and y data located in \'mmwave/data/.X_data\' and \'mmwave/data/.y_data\' files. This data will be used for the evaluating process. If you want to read raw .csv files, provide \'refresh\' (this will take few minutes). Usage: >> eval >> eval refresh ''' if len(args.split()) > 1: error('Unknown arguments.') return if args == '': refresh_data = False elif args == 'refresh': refresh_data = True else: warning(f'Unknown argument: {args}') return X, y = Logger.get_all_data(refresh_data=refresh_data) Logger.get_stats(X, y) X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=.3, stratify=y, random_state=12) if not self.__model_loaded(): self.model.load() print() print('Eval validation dataset:') self.model.evaluate(X_val, y_val) print() print('Eval train dataset:') self.model.evaluate(X_train, y_train) print() print('Eval full dataset:') self.model.evaluate(X, y) print()
def get_response(self): header = self.connection.read(3) packet_size, checksum = struct.unpack('>HB', header) packet_size -= 2 # Compensate for the header payload = self.connection.read(packet_size) self.connection.write(OPCODE.ACK, size=0) # Ack the packet calculated_checksum = sum(payload) & 0xff if (calculated_checksum != checksum): error('Checksum error on received packet.') return return payload
def do_connect(self, args=''): ''' Manually connecting to mmWave Command will ask you to manualy type ports and baudrates. Use <Tab> for autocompletion on available ports and baudrates. Type \'exit\' for exiting manual connection. If you only press enter on baudrates, console will try to find baudrate automatically. Note that this will work only if connection is already opened from both sides. (On mmWave startup only cli port is open, and data port will open only after \'senstrStart\' command) Look \'autoconnect\' command for automatic connection. Usage: >> connect cli port: /dev/ttyACM0 cli rate: 115200 data port: /dev/ttyACM1 data rate: 921600 ''' if args != '': error('Unknown arguments.') return if self.__is_connected(): self.mmwave.disconnect() print() port = self.__get_user_port('cli') if port is not None: self.cli_port = port self.cli_rate = self.__get_user_rate('cli', port) else: return port = self.__get_user_port('data') if port is not None: self.data_port = port self.data_rate = self.__get_user_rate('data', port) else: return self.__mmwave_init()
def do_plot(self, args=''): ''' Start plotter Plotting received frames. Listener should be started before using this command. Use \'stop plot\' to stop this command. Usage: >> plot ''' if args != '': error('Unknown arguments.') return self.plotter_queues['cli'].put('init') with self.plotting_lock: self.plotting = True
def do_redraw(self, args=''): ''' Redraw sample Redrawing last captured gesture file. Possible options: \'up\', \'down\', \'left\', \'right\', \'cw\', \'ccw\', \'s\', \'z\', \'x\' Usage: >> redraw up >> redraw ccw >> redraw z ''' if args == '': error('too few arguments.') return if len(args.split()) > 1: error('Unknown arguments.') return with self.plotting_lock: if not self.plotting: error('Plotter not started.') return if not GESTURE.check(args): warning(f'Unknown argument: {args}') return self.plotter_queues['cli'].put('redraw') self.plotter_queues['cli'].put(args)
def do_flash(self, args=''): ''' Sending .bin files to mmWave Flashing bin files to connected mmWave. It is possible to specify up to 4 meta files. SOP0 and SOP2 have be closed and power reseted before running this command. Look \'connect\' and \'autoconnect\' command for connecting to mmWave. Usage: >> flash xwr16xx_mmw_demo.bin ''' if len(args.split()) > 4: error('Too many arguments.') return if args == '': error('Too few arguments.') return filepaths = [] for arg in args.split(): filepath = os.path.join(self.firmware_dir, arg) if not os.path.isfile(filepath): error(f'File \'{filepath}\' doesn\'t exist.') return filepaths.append(filepath) if not self.__is_connected(): error('Not connected.') return print('Ping mmWave...', end='') response = self.flasher.send_cmd(CMD(OPCODE.PING), resp=False) if response is None: warning('Check if SOP0 and SOP2 are closed, and reset the power.') return print(f'{Fore.GREEN}Done.') print('Get version...', end='') response = self.flasher.send_cmd(CMD(OPCODE.GET_VERSION)) if response is None: return print(f'{Fore.GREEN}Done.') print(f'{Fore.BLUE}Version:', binascii.hexlify(response)) print() self.flasher.flash(filepaths, erase=True) print(f'{Fore.GREEN}Done.')
def do_set_model(self, args=''): ''' Set model type used for prediction. Available models are \'conv\' (convolutional 1D), \'lstm\' (long short-term memory) and \'trans\' (transformer). Default is lstm. Usage: >> set_model conv >> set_model lstm >> set_model trans ''' if len(args.split()) > 1: error('Too many arguments.') return if args not in ['conv', 'lstm', 'trans']: warning(f'Unknown argument: {args}') return self.model_type = args self.__set_model(args)
def do_start(self, args=''): ''' Start listener, plotter and prediction. If mmWave is not configured, default configuration will be send first. Use <Ctrl-C> to stop this command. Usage: >> start ''' if args != '': error('Unknown arguments.') return if not self.__model_loaded(): self.model.load() print() self.do_send(self.default_config) self.do_listen() self.do_plot() self.do_predict() self.do_stop()
def do_autoconnect(self, args=''): ''' Auto connecting to mmWave Automatically looking for \'XDS\' ports and connecting with default baudrates. Auto-detection is only applicable for eval boards with XDS110. Look \'connect\' command for manual connection. Usage: >> autoconnect ''' if args != '': error('Unknown arguments.') return if self.__is_connected(): warning('Already connected.') print('Reconnecting...') self.cli_port = None self.data_port = None self.__mmwave_init()
def do_log(self, args=''): ''' Log data Logging specified gesture. Data will be saved in \'mmwave/data/gesture_foder/sameple_num.csv\' file. Possible options: \'up\', \'down\', \'left\', \'right\', \'cw\', \'ccw\', \'s\', \'z\', \'x\' Usage: >> log up >> log ccw >> log z ''' if args == '': error('too few arguments.') return if len(args.split()) > 1: error('Unknown arguments.') return with self.listening_lock: if not self.listening: error('Listener not started.') return if not GESTURE.check(args): warning(f'Unknown argument: {args}') return self.logger.set_gesture(args) with self.logging_lock: self.logging = True self.__logging_thread().join() with self.logging_lock: self.logging = False
def flash(self, files, file_id=4, storage='SFLASH', mirror_enabled=0, erase=False): if not isinstance(files, list): files = [files] if erase: print('Formating flash...', end='') self.erase() print(f'{Fore.GREEN}Done.') for file in files: file_size = os.path.getsize(file) print( f'Writing {list(Flasher.FILES)[file_id]} [{file_size} bytes]') if file_size < 0 and file_size > Flasher.MAX_FILE_SIZE: error('Invalid file size') return False with open(file, 'rb') as f: resp = self.send_cmd(CMD( OPCODE.OPEN_FILE, data=(struct.pack('>I', file_size) + Flasher.STORAGES[storage] + list(Flasher.FILES.values())[file_id] + struct.pack('>I', mirror_enabled))), get_status=True) if resp != OPCODE.RET_SUCCESS: error('Opening file failed.') return False pbar = tqdm(total=file_size // Flasher.BLOCK_SIZE + 1, desc='Blocks') offset = 0 while offset < file_size: block = f.read(Flasher.BLOCK_SIZE) if (storage == 'SRAM'): resp = self.send_cmd(CMD(OPCODE.WRITE_FILE_RAM, data=block), get_status=True) else: resp = self.send_cmd(CMD(OPCODE.WRITE_FILE, data=block), get_status=True) if resp != OPCODE.RET_SUCCESS: error('Sending file failed.') return False offset += len(block) pbar.update(1) pbar.close() self.send_cmd(CMD(OPCODE.CLOSE_FILE, data=list(Flasher.FILES.values())[file_id]), resp=False) file_id += 1 if file_id > 7: break return True
def do_history(self, args): '''Print a list of commands that have been entered''' if args != '': error('Unknown arguments.') return print(self._hist)