├── Node ├── mwSerial.js └── package.json ├── Python ├── mw0582_algo.py └── mw0582_gui.py ├── README.md └── serial application └── sscom5.13.1.exe /Node/mwSerial.js: -------------------------------------------------------------------------------- 1 | const serialport = require('serialport'); 2 | const Readline = require('@serialport/parser-readline'); 3 | 4 | var portName = process.argv[2]; 5 | 6 | /* 7 | Port Connection 8 | */ 9 | 10 | var parser = new Readline({delimiter: "\n"}); 11 | 12 | function listPorts() { 13 | serialport.list().then( 14 | ports => { 15 | ports.forEach(port => { 16 | console.log(`${port.path}`); 17 | })}, 18 | err => { 19 | console.error('Error listing ports', err); 20 | } 21 | ) 22 | } 23 | 24 | var myPort = serialport(portName, { baudRate: 512000 }); 25 | 26 | myPort.pipe(parser); 27 | 28 | parser.on('data', function(line) { 29 | console.log(line) 30 | }) 31 | -------------------------------------------------------------------------------- /Node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mwNode", 3 | "version": "0.0.1", 4 | "dependencies": { 5 | "serialport": "latest" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Python/mw0582_algo.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | def movingAverage(data, exp, avg): 4 | new_avg = data * exp + (1 - exp) * avg 5 | return new_avg 6 | 7 | def impulseRemoval(arr, size): 8 | m = np.mean(arr) 9 | for x in arr: 10 | if x > mean: 11 | pos = pos + 1 12 | diff = diff + x - mean 13 | elif x < mean: 14 | neg = neg + 1 15 | correct = mean + (pos - neg) * diff / size**2 16 | return correct 17 | 18 | def dcRemoval(data, exp, avg): 19 | new_avg = data * exp + (1 - exp) * avg 20 | base = data - new_avg 21 | return base, new_avg 22 | 23 | def envelopExtract(data, w_dc, w_env, avg, env_avg): 24 | base, new_dc_avg = dcRemoval(data, w_dc, avg) 25 | new_env_avg = movingAverage(np.abs(base), w_env, env_avg) 26 | return new_dc_avg, new_env_avg 27 | 28 | -------------------------------------------------------------------------------- /Python/mw0582_gui.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import matplotlib 3 | matplotlib.use("TkAgg") 4 | from matplotlib import pyplot as plt 5 | from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg 6 | import matplotlib.animation as animation 7 | import serial 8 | from serial.tools.list_ports import comports 9 | import numpy as np 10 | import re 11 | from mw0582_algo import * 12 | 13 | #If we are using python 2.7 or under 14 | if sys.version_info[0] < 3: 15 | import Tkinter as tk 16 | import ttk 17 | #If we are using python 3.0 or above 18 | elif sys.version_info[0] >= 3: 19 | import tkinter as tk 20 | from tkinter import ttk 21 | 22 | 23 | class SensingGUI(tk.Frame): 24 | """docstring for SensingGUI""" 25 | 26 | def define_var(self): 27 | 28 | self.s = None 29 | 30 | self.triggerVar = tk.StringVar(self) 31 | self.triggerVar.set("Trigger") 32 | self.opBoxVar = tk.StringVar(self) 33 | self.opBoxVar.set("-") 34 | self.labelVar = tk.StringVar(self) 35 | self.labelVar.set("Not connected.") 36 | 37 | self.avgCheck = tk.IntVar(self) 38 | self.irCheck = tk.IntVar(self) 39 | self.dcrCheck = tk.IntVar(self) 40 | self.envCheck = tk.IntVar(self) 41 | 42 | self.checkButtons = [self.avgCheck,self.irCheck,self.dcrCheck,self.envCheck] 43 | 44 | def app_quit(self): 45 | self.master.destroy() 46 | self.master.quit() 47 | if self.s is not None: 48 | self.s.flush() 49 | self.s.close() 50 | 51 | def create_widgets(self, plotObject): 52 | self.menubar = tk.Menu(self.master) 53 | self.filemenu = tk.Menu(self.menubar, tearoff=0) 54 | self.filemenu.add_command(label="Exit", command=self.app_quit) 55 | 56 | # Left Frame 57 | self.leftFrame = tk.Frame(self.master, padx=10, pady=10) 58 | self.leftFrame.grid(row=0, column=0, sticky='N,W,S,E', rowspan=20) 59 | 60 | # Plot Frame 61 | # For data visualization 62 | 63 | self.plotFrame = tk.LabelFrame(self.leftFrame, padx=10, pady=10, borderwidth=2, text='Raw Plot', labelanchor='nw', relief=tk.RIDGE) 64 | self.plotFrame.grid(row=0, column=0) 65 | 66 | 67 | self.canvas = FigureCanvasTkAgg(plotObject.drawArea, master=self.plotFrame) 68 | self.canvas._tkcanvas.grid(row=0, column=0) 69 | self.canvas.draw() 70 | 71 | # Right Frame 72 | self.rightFrame = tk.Frame(self.master, padx=10, pady=10) 73 | self.rightFrame.grid(row=0, column=1, sticky='N,W,S,E', rowspan=20) 74 | 75 | self.operFrame = tk.Frame(self.rightFrame, padx=10, pady=10) 76 | self.operFrame.grid(row=0, column=0, columnspan=2, sticky='w,e') 77 | 78 | if sys.platform.startswith('darwin'): 79 | # portChoices = tuple([p[0].replace('/cu.', '/tty.') for p in comports() if p[0].find('SLAB') >= 0]) 80 | portChoices = tuple([p[0].replace('/cu.', '/tty.') for p in comports()]) 81 | else: 82 | portChoices = tuple([p[0].replace('/cu.', '/tty.') for p in comports()]) 83 | 84 | self.option = tk.OptionMenu(self.operFrame, self.opBoxVar, *portChoices) 85 | self.option.grid(row=0, column=0,sticky='w') 86 | 87 | def optionChanged(*args): 88 | print(self.opBoxVar.get()) 89 | self.s = serial.Serial(self.opBoxVar.get(), 512000) 90 | self.s.flush() 91 | if self.s is not None: 92 | self.labelVar.set("Device connected.") 93 | self.runButton.config(state=tk.NORMAL) 94 | self.rfLabel.config(state=tk.NORMAL) 95 | # self.rfScale.config(state=tk.NORMAL) 96 | self.powerLabel.config(state=tk.NORMAL) 97 | self.powerScale.config(state=tk.NORMAL) 98 | self.gainLabel.config(state=tk.NORMAL) 99 | self.gainScale.config(state=tk.NORMAL) 100 | self.delayLabel.config(state=tk.NORMAL) 101 | self.delayScale.config(state=tk.NORMAL) 102 | self.avgCheckbutton.config(state=tk.NORMAL) 103 | # self.avgTapEntry.config(state=tk.NORMAL) 104 | self.irCheckbutton.config(state=tk.NORMAL) 105 | self.irTapEntry.config(state=tk.NORMAL) 106 | self.dcrCheckbutton.config(state=tk.NORMAL) 107 | # self.dcrTapEntry.config(state=tk.NORMAL) 108 | self.envCheckbutton.config(state=tk.NORMAL) 109 | # self.envTapEntry.config(state=tk.NORMAL) 110 | 111 | self.opBoxVar.trace("w", optionChanged) 112 | 113 | # Connection status label 114 | self.connLabel = tk.Label(self.operFrame, textvariable=self.labelVar) 115 | self.connLabel.grid(row=0, column=1,sticky='w') 116 | 117 | # Button 118 | self.pause = True 119 | self.ani = None 120 | 121 | def trigger(): 122 | self.pause ^= True 123 | if self.pause: 124 | self.ani.event_source.stop() 125 | self.triggerVar.set("Trigger") 126 | else: 127 | self.ani = animation.FuncAnimation(plotObject.drawArea, plotObject.update, fargs=(self.s, self.checkButtons, plotObject.a0,), interval=1, blit=True) 128 | self.triggerVar.set("Pause") 129 | 130 | self.runButton = tk.Button(self.operFrame, textvariable=self.triggerVar, width=10, height=3, fg='red', command=trigger) 131 | # self.calibButton = tk.Button(self.operFrame, text='Re-Calibrate', width=10, height=3) 132 | self.runButton.grid(row=1, column=0,sticky='w') 133 | self.runButton.config(state=tk.DISABLED) 134 | # self.calibButton.grid(row=1, column=1,sticky='w') 135 | 136 | # Setting Frame 137 | self.settingFrame = tk.LabelFrame(self.rightFrame, padx=10, pady=10, borderwidth=2, text='Setting', labelanchor='nw', relief=tk.RIDGE) 138 | self.settingFrame.grid(row=1, column=0, sticky='w,e') 139 | 140 | # RF Range setting 141 | self.rfLabel = tk.Label(self.settingFrame, text='RF Range (in GHz)') 142 | self.rfLabel.grid(row=0, column=0, sticky='s,e') 143 | self.rfLabel.config(state=tk.DISABLED) 144 | self.rfScale = tk.Scale(self.settingFrame, 145 | from_ = 5.725, 146 | to = 5.875, 147 | orient = tk.HORIZONTAL, 148 | showvalue = 1, 149 | length = 200, 150 | resolution = 0.005, 151 | command = self.rf_scale_click 152 | ) 153 | self.rfScale.grid(row=0, column=1) 154 | self.rfScale.config(state=tk.DISABLED) 155 | # TX Power setting 156 | self.powerLabel = tk.Label(self.settingFrame, text='TX Power') 157 | self.powerLabel.grid(row=1, column=0, sticky='s,e') 158 | self.powerLabel.config(state=tk.DISABLED) 159 | self.powerScale = tk.Scale(self.settingFrame, 160 | from_ = 0, 161 | to = 7, 162 | orient = tk.HORIZONTAL, 163 | showvalue = 1, 164 | length = 200, 165 | resolution = 1, 166 | command = self.power_scale_click 167 | ) 168 | self.powerScale.grid(row=1, column=1) 169 | self.powerScale.config(state=tk.DISABLED) 170 | 171 | # RX Gain setting 172 | self.gainLabel = tk.Label(self.settingFrame, text='RX Gain') 173 | self.gainLabel.grid(row=2, column=0,sticky='s,e') 174 | self.gainLabel.config(state=tk.DISABLED) 175 | 176 | self.gainScale = tk.Scale(self.settingFrame, 177 | from_ = 0, 178 | to = 7, 179 | orient = tk.HORIZONTAL, 180 | showvalue = 1, 181 | length = 200, 182 | resolution = 1, 183 | command = self.gain_scale_click 184 | ) 185 | self.gainScale.grid(row=2, column=1) 186 | self.gainScale.config(state=tk.DISABLED) 187 | 188 | # Delay setting 189 | self.delayLabel = tk.Label(self.settingFrame, text='Delay Time (in s)') 190 | self.delayLabel.grid(row=3, column=0,sticky='s,e') 191 | self.delayLabel.config(state=tk.DISABLED) 192 | self.delayScale = tk.Scale(self.settingFrame, 193 | from_ = 0, 194 | to = 3599, 195 | orient = tk.HORIZONTAL, 196 | showvalue = 1, 197 | length = 200, 198 | resolution = 100, 199 | command = self.delay_scale_click 200 | ) 201 | self.delayScale.grid(row=3, column=1) 202 | self.delayScale.config(state=tk.DISABLED) 203 | 204 | # Processing Frame 205 | self.processFrame = tk.LabelFrame(self.rightFrame, padx=10, pady=10, borderwidth=2, text='Process', labelanchor='nw', relief=tk.RIDGE) 206 | self.processFrame.grid(row=2, column=0, columnspan=3, sticky='w,e') 207 | 208 | # Process Selection 209 | 210 | self.avgCheckbutton = tk.Checkbutton(self.processFrame, 211 | text = 'Moving Average', 212 | variable = self.avgCheck, 213 | onvalue = 1, 214 | offvalue = 0, 215 | command = self.avgcheckbutton_click 216 | ) 217 | self.avgCheckbutton.grid(row=0, column=0, sticky='w') 218 | 219 | self.irCheckbutton = tk.Checkbutton(self.processFrame, 220 | text = 'Impulse Removal', 221 | variable = self.irCheck, 222 | onvalue = 1, 223 | offvalue = 0, 224 | command = self.ircheckbutton_click 225 | ) 226 | self.irCheckbutton.grid(row=1, column=0, sticky='w') 227 | # self.irCheckbutton.config(state=tk.DISABLED) 228 | self.irLabel = tk.Label(self.processFrame, text='Taps:') 229 | self.irLabel.grid(row=1, column=1) 230 | self.irTapEntry = tk.Entry(self.processFrame, relief=tk.RIDGE, width=10) 231 | self.irTapEntry.insert(0, '5') 232 | self.irTapEntry.grid(row=1, column=2, sticky='e') 233 | self.irTapEntry.config(state=tk.DISABLED) 234 | 235 | self.dcrCheckbutton = tk.Checkbutton(self.processFrame, 236 | text = 'DC Removal', 237 | variable = self.dcrCheck, 238 | onvalue = 1, 239 | offvalue = 0, 240 | command = self.dcrcheckbutton_click 241 | ) 242 | self.dcrCheckbutton.grid(row=2, column=0, sticky='w') 243 | # self.dcrCheckbutton.config(state=tk.DISABLED) 244 | # self.dcrLabel = tk.Label(self.processFrame, text='Taps:') 245 | # self.dcrLabel.grid(row=2, column=1) 246 | # self.dcrTapEntry = tk.Entry(self.processFrame, relief=tk.RIDGE, width=10) 247 | # self.dcrTapEntry.insert(0, '5') 248 | # self.dcrTapEntry.grid(row=2, column=2, sticky='e') 249 | # self.dcrTapEntry.config(state=tk.DISABLED) 250 | 251 | self.envCheckbutton = tk.Checkbutton(self.processFrame, 252 | text = 'Envelop Extraction', 253 | variable = self.envCheck, 254 | onvalue = 1, 255 | offvalue = 0, 256 | command = self.envcheckbutton_click 257 | ) 258 | self.envCheckbutton.grid(row=3, column=0, sticky='w') 259 | # self.envCheckbutton.config(state=tk.DISABLED) 260 | # self.envLabel = tk.Label(self.processFrame, text='Taps:') 261 | # self.envLabel.grid(row=3, column=1) 262 | # self.envTapEntry = tk.Entry(self.processFrame, relief=tk.RIDGE, width=10) 263 | # self.envTapEntry.insert(0, '5') 264 | # self.envTapEntry.grid(row=3, column=2, sticky='e') 265 | # self.envTapEntry.config(state=tk.DISABLED) 266 | 267 | def rf_scale_click(self, v): 268 | if self.s is not None: 269 | command = "AT+FREQ={}".format(v) 270 | self.s.write(command.encode('utf-8')) 271 | line = self.s.read(2) 272 | if line is 'OK': 273 | tk.messagebox.showerror("Error", "Parameter set failed.") 274 | else: 275 | tk.messagebox.showinfo("Success", "Parameter set successful!") 276 | def power_scale_click(self, v): 277 | if self.s is not None: 278 | command = "AT+PA={}".format(v) 279 | self.s.write(command.encode('utf-8')) 280 | def gain_scale_click(self,v): 281 | if self.s is not None: 282 | command = "AT+REVGAIN={}".format(v) 283 | self.s.write(command.encode('utf-8')) 284 | def delay_scale_click(self, v): 285 | if self.s is not None: 286 | command = "AT+DELAY={}".format(v) 287 | self.s.write(command.encode('utf-8')) 288 | def avgcheckbutton_click(self): 289 | self.irCheckbutton.deselect() 290 | self.dcrCheckbutton.deselect() 291 | self.envCheckbutton.deselect() 292 | if(self.avgCheck.get()): 293 | self.avgCheckbutton.select() 294 | else: 295 | self.avgCheckbutton.deselect() 296 | 297 | def ircheckbutton_click(self, plotObject): 298 | self.dcrCheckbutton.deselect() 299 | self.envCheckbutton.deselect() 300 | self.avgCheckbutton.deselect() 301 | self.irCheckbutton.select() 302 | if(self.irCheck.get()): 303 | self.irCheckbutton.select() 304 | else: 305 | self.irCheckbutton.deselect() 306 | 307 | def dcrcheckbutton_click(self): 308 | self.irCheckbutton.deselect() 309 | self.envCheckbutton.deselect() 310 | self.avgCheckbutton.deselect() 311 | if(self.dcrCheck.get()): 312 | self.dcrCheckbutton.select() 313 | else: 314 | self.dcrCheckbutton.deselect() 315 | 316 | def envcheckbutton_click(self): 317 | self.irCheckbutton.deselect() 318 | self.dcrCheckbutton.deselect() 319 | self.avgCheckbutton.deselect() 320 | if(self.envCheck.get()): 321 | self.envCheckbutton.select() 322 | else: 323 | self.envCheckbutton.deselect() 324 | 325 | def __init__(self, master=None): 326 | tk.Frame.__init__(self,master) 327 | self.appPlot = GUIPlot(size=500) 328 | self.master = master 329 | self.define_var() 330 | self.create_widgets(self.appPlot) 331 | 332 | 333 | class GUIPlot: 334 | # constructor 335 | def __init__(self, size): 336 | self.drawArea = plt.figure() 337 | self.ax = plt.axes(ylim=(0, 2000)) 338 | self.ax.axvline(linewidth=4, color='r', x=250) 339 | self.arr = np.zeros(size) 340 | self.a0, = self.ax.plot(range(500), self.arr) 341 | self.avg = 0; 342 | self.dcavg = 0; 343 | self.env_avg = 0; 344 | self.base = 0; 345 | 346 | # update plot 347 | def update(self, frameNum, ser, checkButtons, a0): 348 | try: 349 | line = ser.read(300) 350 | try: 351 | data = [int(x, 16) for x in re.findall(' (?!f)\w{4}', line.decode('utf-8'))] 352 | # print(data) 353 | # self.arr = np.append(self.arr[len(data):], data) 354 | new_data = data[-1] 355 | 356 | if(checkButtons[0].get()): 357 | self.avg = movingAverage(self.arr[250], 0.2, self.avg) 358 | self.arr = np.concatenate((self.arr[1:250], self.avg, self.arr[251:], new_data), axis=None) 359 | elif(checkButtons[1].get()): 360 | pass 361 | elif(checkButtons[2].get()): 362 | self.base, self.dcavg = dcRemoval(self.arr[250], 0.7, self.dcavg) 363 | self.arr = np.concatenate((self.arr[1:250], self.base, self.arr[251:], new_data), axis=None) 364 | elif(checkButtons[3].get()): 365 | self.dcavg, self.env_avg = envelopExtract(self.arr[250], 0.2, 0.7, self.dcavg, self.env_avg) 366 | self.arr = np.concatenate((self.arr[1:250], self.env_avg, self.arr[251:], new_data), axis=None) 367 | else: 368 | self.arr = np.concatenate((self.arr[1:], new_data), axis=None) 369 | 370 | self.a0.set_ydata(self.arr) 371 | 372 | except Exception: 373 | pass 374 | except KeyboardInterrupt: 375 | print('exiting..') 376 | return self.a0, 377 | 378 | if __name__ == '__main__': 379 | root = tk.Tk() 380 | root.title('Maxustech Sensing GUI') 381 | app = SensingGUI(master=root) 382 | 383 | def on_close(): 384 | try: 385 | app.app_quit() 386 | except Exception: 387 | print("Exiting...") 388 | app.app_quit() 389 | 390 | root.protocol("WM_DELETE_WINDOW", on_close) 391 | app.mainloop() 392 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MWTool for mw series radar 2 | 3 | 4 | 5 |
6 |
7 |
22 |
23 |