diff options
Diffstat (limited to 'gps_python_html/gps_tracker_gui_v1.0.py')
-rw-r--r-- | gps_python_html/gps_tracker_gui_v1.0.py | 349 |
1 files changed, 349 insertions, 0 deletions
diff --git a/gps_python_html/gps_tracker_gui_v1.0.py b/gps_python_html/gps_tracker_gui_v1.0.py new file mode 100644 index 0000000..295caa9 --- /dev/null +++ b/gps_python_html/gps_tracker_gui_v1.0.py | |||
@@ -0,0 +1,349 @@ | |||
1 | import time | ||
2 | import serial | ||
3 | import webbrowser | ||
4 | import os | ||
5 | from serial.tools import list_ports | ||
6 | import datetime | ||
7 | from tkinter import * | ||
8 | from tkinter import ttk | ||
9 | from tkinter import messagebox | ||
10 | from tkinter.filedialog import askopenfilename | ||
11 | from tkinter.filedialog import asksaveasfilename | ||
12 | from serial.tools import list_ports | ||
13 | import csv | ||
14 | import re | ||
15 | |||
16 | # | ||
17 | # /****************************************************************************** | ||
18 | |||
19 | # @file gps_tracker_gui.py | ||
20 | |||
21 | # @brief front end file | ||
22 | |||
23 | # Group: WCS LPC | ||
24 | # $Target Device: DEVICES $ | ||
25 | |||
26 | # ****************************************************************************** | ||
27 | # $License: BSD3 2016 $ | ||
28 | # ****************************************************************************** | ||
29 | # $Release Name: PACKAGE NAME $ | ||
30 | # $Release Date: PACKAGE RELEASE DATE $ | ||
31 | # *****************************************************************************/ | ||
32 | # | ||
33 | |||
34 | class App: | ||
35 | |||
36 | # GLOBALS | ||
37 | nodes = [] | ||
38 | known_addresses = [] | ||
39 | gpsTurnedOn = FALSE | ||
40 | gpsStayOn = FALSE | ||
41 | ser = serial.Serial() | ||
42 | comport = '' | ||
43 | csvName = '' | ||
44 | append = FALSE | ||
45 | cancel = FALSE | ||
46 | serReads = [1] | ||
47 | |||
48 | # DEFINITIONS | ||
49 | class Node: | ||
50 | def __init__(self, address): | ||
51 | self.addr = address | ||
52 | self.gps = [] | ||
53 | |||
54 | class Fix: | ||
55 | lat = '' | ||
56 | lng = '' | ||
57 | latDM = '' | ||
58 | lngDM = '' | ||
59 | alt = '' | ||
60 | rssi = '' | ||
61 | fixTime = '' # Time GPS fix was taken | ||
62 | plotTime = '' # Time fix is plotted | ||
63 | |||
64 | # FUNCTIONS | ||
65 | def writeData(self, addr, fix, overwrite_csv): | ||
66 | f = open('gpsdata.js', 'rb+') | ||
67 | f.seek(-1, 2) | ||
68 | f.truncate() | ||
69 | f.close() | ||
70 | f = open('gpsdata.js', 'r+') | ||
71 | f.seek(0, 2) | ||
72 | f.write('{ addr: ' + str(addr) + ', ' \ | ||
73 | + 'lat: ' + fix.lat + ', ' \ | ||
74 | + 'lng: ' + fix.lng + ', ' \ | ||
75 | + 'alt: ' + fix.alt + ', ' \ | ||
76 | + 'rssi: ' + fix.rssi + ', ' \ | ||
77 | + 'time: \'' + fix.fixTime + '\' },\n]') | ||
78 | f.close() | ||
79 | now = datetime.datetime.utcnow() | ||
80 | |||
81 | #if(not overwrite_csv): | ||
82 | with open(self.csvName, 'a', newline='') as csvfile: | ||
83 | writer = csv.writer(csvfile) | ||
84 | writer.writerow((str(addr), fix.lat, fix.lng, fix.alt, fix.rssi, fix.fixTime, str(now.year) \ | ||
85 | + '-' + str(now.month).zfill(2) \ | ||
86 | + '-' + str(now.day).zfill(2), fix.latDM, fix.lngDM, fix.plotTime)) | ||
87 | |||
88 | def addFix(self, addr, fix, overwrite_csv): | ||
89 | if addr not in self.known_addresses: | ||
90 | self.nodes.append(self.Node(addr)) | ||
91 | self.known_addresses.append(addr) | ||
92 | self.nodes[self.known_addresses.index(addr)].gps.append(fix) | ||
93 | self.writeData(addr, fix, overwrite_csv) | ||
94 | |||
95 | def serialUpdate(self, event): | ||
96 | ports = sorted(list_ports.comports()) | ||
97 | self.port_names = [] | ||
98 | for i, port in enumerate(ports): | ||
99 | if "Application" in port.description: | ||
100 | self.port_names.append(port.device) | ||
101 | self.serialBox['values'] = self.port_names | ||
102 | |||
103 | def serialGet(self, event): | ||
104 | self.comport = self.serialBox.get() | ||
105 | |||
106 | def overwriteCSV(self, ow): | ||
107 | self.overwrite = TRUE | ||
108 | ow.destroy() | ||
109 | |||
110 | def appendCSV(self, ow): | ||
111 | self.overwrite = FALSE | ||
112 | ow.destroy() | ||
113 | |||
114 | def cancelCSV(self, ow): | ||
115 | self.cancel = TRUE | ||
116 | ow.destroy() | ||
117 | |||
118 | # MAIN PROGRAM FUNCTIONS | ||
119 | def startGPS(self): | ||
120 | |||
121 | self.serialBox.state = "disabled" | ||
122 | |||
123 | if(self.gpsTurnedOn == FALSE): | ||
124 | self.gpsTurnedOn = TRUE | ||
125 | self.overwrite = FALSE | ||
126 | self.cancel = FALSE | ||
127 | |||
128 | self.csvName = asksaveasfilename(initialdir = "/csv", title = "New csv File", defaultextension = ".csv",filetypes = (("csv files","*.csv"),("all files","*.*"))) | ||
129 | |||
130 | # Overwrite or append file? | ||
131 | if os.path.isfile(self.csvName): | ||
132 | ow = Toplevel() | ||
133 | ow.title('Overwrite') | ||
134 | ow.geometry('300x100') | ||
135 | frame1 = Frame(ow) | ||
136 | frame2 = Frame(ow) | ||
137 | frame1.place(relx=0.05, rely=0.1, relheight=0.4, relwidth=0.9) | ||
138 | frame2.place(relx=0.05, rely=0.5, relheight=0.4, relwidth=0.9) | ||
139 | |||
140 | Label(frame1, text="Overwrite or append to existing file?").place(relx=0.5, rely=0, anchor=N) | ||
141 | Button(frame2, text='Overwrite', command=lambda: self.overwriteCSV(ow)).place(relx=0, relwidth=0.3, anchor=NW) | ||
142 | Button(frame2, text='Append', command=lambda: self.appendCSV(ow)).place(relx=0.5, relwidth=0.3, anchor=N) | ||
143 | Button(frame2, text='Cancel', command=lambda: self.cancelCSV(ow)).place(relx=1, relwidth=0.3, anchor=NE) | ||
144 | self.master.wait_window(ow) | ||
145 | else: | ||
146 | self.overwrite = TRUE | ||
147 | |||
148 | if self.cancel == FALSE: | ||
149 | if self.overwrite: | ||
150 | f = open(self.csvName, 'w') | ||
151 | f.truncate() | ||
152 | f.close() | ||
153 | with open(self.csvName, 'w', newline='') as csvfile: | ||
154 | writer = csv.writer(csvfile) | ||
155 | writer.writerow(('Node Address', 'Latitude (°)', 'Longitude (°)', \ | ||
156 | 'Altitude (m)', 'RSSI (dBm)', 'Time of Fix (UTC)', 'Date', \ | ||
157 | 'Latitude (DM)', 'Longitude (DM)', 'Time of Plot (UTC)')) | ||
158 | |||
159 | self.ser = serial.Serial(self.comport, 115200, timeout=0.5) | ||
160 | |||
161 | # Erase js file | ||
162 | f = open('gpsdata.js', 'r+') | ||
163 | f.truncate() | ||
164 | f.write('var gps = [\n]') | ||
165 | f.close() | ||
166 | |||
167 | webbrowser.open('cc13xx_map.html') | ||
168 | |||
169 | self.gpsStayOn = TRUE | ||
170 | |||
171 | else: | ||
172 | self.stopGPS() | ||
173 | |||
174 | if self.gpsStayOn: | ||
175 | in_str = '' | ||
176 | |||
177 | if self.ser.in_waiting > 0: | ||
178 | try: | ||
179 | in_byte = self.ser.read(1) | ||
180 | |||
181 | except: | ||
182 | self.stopGPS() | ||
183 | |||
184 | else: | ||
185 | in_str = in_byte.decode('utf-8') | ||
186 | |||
187 | if in_str == '$': | ||
188 | try: | ||
189 | in_byte = self.ser.read(18) | ||
190 | |||
191 | except TypeError as e: | ||
192 | in_str = '' | ||
193 | messagebox.showinfo('Error', 'COM Port Disconnected') | ||
194 | self.stopGPS() | ||
195 | |||
196 | else: | ||
197 | address = in_byte[0] | ||
198 | rssi = int.from_bytes(bytes([in_byte[1]]), byteorder='little', signed=True) | ||
199 | latDM = int.from_bytes(in_byte[2:4], byteorder='little', signed=True) | ||
200 | latm = int.from_bytes(in_byte[4:7], byteorder='little', signed=False) | ||
201 | lngDM = int.from_bytes(in_byte[7:9], byteorder='little', signed=True) | ||
202 | lngm = int.from_bytes(in_byte[9:12], byteorder='little', signed=False) | ||
203 | altA = int.from_bytes(in_byte[12:14], byteorder='little', signed=False) | ||
204 | alta = in_byte[14] | ||
205 | time = in_byte[15:] | ||
206 | |||
207 | gps = self.Fix() | ||
208 | |||
209 | latmstr = str(latm).zfill(6) | ||
210 | lngmstr = str(lngm).zfill(6) | ||
211 | |||
212 | gps.latDM = str(latDM)[0:-2] + ' ' + str(latDM)[-2:] + '.' + latmstr | ||
213 | gps.lngDM = str(lngDM)[0:-2] + ' ' + str(lngDM)[-2:] + '.' + lngmstr | ||
214 | gps.lat = str(latDM)[0:-2] \ | ||
215 | + str(float(str(latDM)[-2:] \ | ||
216 | + '.' + latmstr)/60)[1:10] | ||
217 | gps.lng = str(lngDM)[0:-2] \ | ||
218 | + str(float(str(lngDM)[-2:] \ | ||
219 | + '.' \ | ||
220 | + lngmstr)/60)[1:10] | ||
221 | gps.alt = str(altA) + '.' + str(alta) | ||
222 | gps.fixTime = str(time[0]).zfill(2) + ':' \ | ||
223 | + str(time[1]).zfill(2) + ':' \ | ||
224 | + str(time[2]).zfill(2) | ||
225 | gps.rssi = str(rssi) | ||
226 | |||
227 | now = datetime.datetime.utcnow() | ||
228 | gps.plotTime = str(now.hour).zfill(2) + ':' \ | ||
229 | + str(now.minute).zfill(2) + ':' \ | ||
230 | + str(now.second).zfill(2) | ||
231 | |||
232 | self.addFix(address, gps, self.overwrite) | ||
233 | |||
234 | self.master.after(100, self.startGPS) | ||
235 | else: | ||
236 | self.gpsTurnedOn = FALSE | ||
237 | |||
238 | def stopGPS(self): | ||
239 | if self.gpsStayOn: | ||
240 | self.ser.close() | ||
241 | self.serialBox.set('') | ||
242 | self.serialBox.state = "enabled" | ||
243 | self.gpsStayOn = FALSE | ||
244 | self.csvName = '' | ||
245 | |||
246 | def saveCSV(self): | ||
247 | f_name = asksaveasfilename(initialdir="/", title="Filename to save as", \ | ||
248 | defaultextension=".csv", \ | ||
249 | filetypes=(("csv files", "*.csv"), ("all files", "*.*"))) | ||
250 | with open(f_name, 'w', newline='') as csvfile: | ||
251 | writer = csv.writer(csvfile) | ||
252 | writer.writerow(('Node Address', 'Latitude (°)', 'Longitude (°)', 'Altitude (m)', \ | ||
253 | 'RSSI (dBm)', 'Time of Fix (UTC)', 'Date', 'Latitude (DM)', \ | ||
254 | 'Longitude (DM)', 'Time of Plot (UTC)')) | ||
255 | for n in self.nodes: | ||
256 | for fix in n.gps: | ||
257 | writer.writerow((str(n.addr), fix.lat, fix.lng, fix.alt, fix.rssi, \ | ||
258 | fix.fixTime, str(now.year) + '-' + str(now.month).zfill(2) \ | ||
259 | + '-' + str(now.day).zfill(2), fix.latDM, fix.lngDM, fix.plotTime)) | ||
260 | |||
261 | def openCSV(self): | ||
262 | # Reset variables | ||
263 | self.nodes = [] | ||
264 | self.known_addresses = [] | ||
265 | |||
266 | f = open('gpsdata.js', 'r+') | ||
267 | f.truncate() | ||
268 | f.write('var gps = [\n]') | ||
269 | f.close() | ||
270 | |||
271 | f = open('gpsdata.js', 'rb+') | ||
272 | f.seek(-1, 2) | ||
273 | f.truncate() | ||
274 | f = open('gpsdata.js', 'r+') | ||
275 | f.seek(0, 2) | ||
276 | |||
277 | self.csvName = askopenfilename(initialdir="/csv", title="Select file to open", \ | ||
278 | filetypes=(("csv files", "*.csv"), ("all files", "*.*"))) | ||
279 | with open(self.csvName) as csvfile: | ||
280 | readCSV = csv.reader(csvfile) | ||
281 | for i, row in enumerate(readCSV): | ||
282 | if i > 0: | ||
283 | f.write('{ addr: ' + row[0] + ', ' \ | ||
284 | + 'lat: ' + row[1] + ', ' \ | ||
285 | + 'lng: ' + row[2] + ', ' \ | ||
286 | + 'alt: ' + row[3] + ', ' \ | ||
287 | + 'rssi: ' + row[4] + ', ' \ | ||
288 | + 'time: \'' + row[5] + '\' },\n') | ||
289 | f.write(']') | ||
290 | f.close() | ||
291 | self.csvName = '' | ||
292 | |||
293 | webbrowser.open('cc13xx_map.html') | ||
294 | |||
295 | def __init__(self, master): | ||
296 | |||
297 | self.master = master | ||
298 | self.master.title('GPS Range Test') | ||
299 | self.master.resizable(width=False, height=False) | ||
300 | self.master.geometry("275x240") | ||
301 | |||
302 | #title = Label(root,text='TI CC13xx GPS to Map') | ||
303 | self.frame1 = Frame(root) | ||
304 | self.frame2 = Frame(root) | ||
305 | self.frame4 = Frame(root) | ||
306 | self.frame5 = Frame(root) | ||
307 | |||
308 | self.frame1.place(relx=0.1, rely=0.1, relheight=0.15, relwidth=0.8) | ||
309 | self.frame2.place(relx=0.1, rely=0.25, relheight=0.25, relwidth=0.8) | ||
310 | self.frame4.place(relx=0.1, rely=0.5, relheight=0.2, relwidth=0.8) | ||
311 | self.frame5.place(relx=0.1, rely=0.73, relheight=0.2, relwidth=0.8) | ||
312 | |||
313 | # title | ||
314 | self.Title = Label(self.frame1, text="TI CC13xx GPS Range Test", \ | ||
315 | justify=CENTER, font=("Arial Bold", 10)) | ||
316 | self.Title.pack(fill=BOTH) | ||
317 | |||
318 | # Buttons | ||
319 | self.bStart = Button(self.frame4, state=NORMAL, text="Start", \ | ||
320 | command=self.startGPS).place(rely=0.15, relheight=.7, relwidth=.45) | ||
321 | self.bStop = Button(self.frame4, state=NORMAL, text="Stop", \ | ||
322 | command=self.stopGPS).place(relx=0.55, rely=0.15, relheight=.7, relwidth=.45) | ||
323 | self.bOpen = Button(self.frame5, state=NORMAL, text="Open .csv", \ | ||
324 | command=self.openCSV).place(rely=0.15, relheight=.7, relwidth=1) | ||
325 | |||
326 | # Get COM ports | ||
327 | self.port_names = [] | ||
328 | ports = sorted(list_ports.comports()) | ||
329 | for i, port in enumerate(ports): | ||
330 | if "Application" in port.description: | ||
331 | self.port_names.append(port.device) | ||
332 | |||
333 | # Com Ports Dropdown | ||
334 | self.serialLabel = Label(self.frame2, text="Serial Port", font=("Arial", 8)) | ||
335 | self.serialLabel.place(rely=0) | ||
336 | self.serialBox = ttk.Combobox(self.frame2) | ||
337 | self.serialBox.bind("<<ComboboxSelected>>", self.serialGet) | ||
338 | self.serialBox.bind("<Button-1>", self.serialUpdate) | ||
339 | self.serialBox.place(rely=0.4, relwidth=1) | ||
340 | self.serialBox['values'] = self.port_names | ||
341 | |||
342 | # Create CSV Folder | ||
343 | if not os.path.isdir('csv'): | ||
344 | os.makedirs('csv') | ||
345 | |||
346 | # Start GUI | ||
347 | root = Tk() | ||
348 | app = App(root) | ||
349 | root.mainloop() | ||