1 | 1 |
new file mode 100755 |
... | ... |
@@ -0,0 +1,442 @@ |
1 |
+#!/usr/bin/python -u |
|
2 |
+# The -u option turns off block buffering of python output. This assures |
|
3 |
+# that output streams to stdout when output happens. |
|
4 |
+# |
|
5 |
+# Module: ft991.py |
|
6 |
+# |
|
7 |
+# Description: This module contains tables for translating common transceiver |
|
8 |
+# settings to FT991 CAT parameters. Low level serial |
|
9 |
+# communication functions are also handled by this module. In |
|
10 |
+# particular this module handles: |
|
11 |
+# 1. Instantiating a serial connection object |
|
12 |
+# 2. Sending character strings to the serial port |
|
13 |
+# 3. Reading characters from the serial port |
|
14 |
+# 4. Parsing and formatting of FT991 commands |
|
15 |
+# 5. Translating radio operating parameters to CAT |
|
16 |
+# commands, i.e., CTCSS tones. |
|
17 |
+# |
|
18 |
+# Copyright 2019 by Jeff Owrey, Intravisions.com |
|
19 |
+# This program is free software: you can redistribute it and/or modify |
|
20 |
+# it under the terms of the GNU General Public License as published by |
|
21 |
+# the Free Software Foundation, either version 3 of the License, or |
|
22 |
+# (at your option) any later version. |
|
23 |
+# |
|
24 |
+# This program is distributed in the hope that it will be useful, |
|
25 |
+# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
26 |
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
27 |
+# GNU General Public License for more details. |
|
28 |
+# |
|
29 |
+# You should have received a copy of the GNU General Public Licensef |
|
30 |
+# along with this program. If not, see http://www.gnu.org/license. |
|
31 |
+# |
|
32 |
+# Revision History |
|
33 |
+# * v10 24 Nov 2019 by J L Owrey; first release |
|
34 |
+# |
|
35 |
+# This script has been tested with the following |
|
36 |
+# |
|
37 |
+# Python 2.7.15rc1 (default, Nov 12 2018, 14:31:15) |
|
38 |
+# [GCC 7.3.0] on linux2 |
|
39 |
+#2345678901234567890123456789012345678901234567890123456789012345678901234567890 |
|
40 |
+ |
|
41 |
+import sys, serial, time |
|
42 |
+ |
|
43 |
+# General constant defines |
|
44 |
+_INTERFACE_TIMEOUT = 0.1 # seconds |
|
45 |
+_SERIAL_READ_TIMEOUT = 0.1 # seconds |
|
46 |
+_SERIAL_READ_BUFFER_LENGTH = 1024 # characters |
|
47 |
+ |
|
48 |
+# Define globals |
|
49 |
+verbose = False |
|
50 |
+debug = False |
|
51 |
+ptrDevice = None |
|
52 |
+ |
|
53 |
+# Define lookup tables for common transceiver settings. Common settings |
|
54 |
+# such as modulation mode, repeater offset direction, DCS/CTCSS mode, |
|
55 |
+# CTCSS tone, and DCS code are translated to the repective FT991 parameter |
|
56 |
+# value. |
|
57 |
+ |
|
58 |
+# Modulation modes |
|
59 |
+dMode = { 'LSB':'1', 'USB':'2', 'CW':'3', 'FM':'4', 'AM':'5', |
|
60 |
+ 'RTTY-LSB':'6', 'CW-R':'7', 'DATA-LSB':'8', 'RTTY-USB':'9', |
|
61 |
+ 'DATA-FM':'A', 'FM-N':'B', 'DATA-USB':'C', 'AM-N':'D', |
|
62 |
+ 'C4FM':'E' } |
|
63 |
+ |
|
64 |
+# Repeater shift direction |
|
65 |
+dShift = { 'OFF':'0', '+RPT':'1', '-RPT':'2' } |
|
66 |
+ |
|
67 |
+# Power settings |
|
68 |
+dPower = { 'LOW':5, 'MID':020, 'HIGH':50, 'MAX':100 } |
|
69 |
+ |
|
70 |
+# Repeater signaling modes |
|
71 |
+dEncode = { 'OFF':'0', 'ENC/DEC':'1', 'TONE ENC':'2', |
|
72 |
+ 'DCS ENC/DEC':'4', 'DCS':'3' } |
|
73 |
+ |
|
74 |
+# CTCSS Tones |
|
75 |
+dTones = { '67.0 Hz':'000', '69.3 Hz':'001', '71.9 Hz':'002', |
|
76 |
+ '74.4 Hz':'003', '77.0 Hz':'004', '79.7 Hz':'005', |
|
77 |
+ '82.5 Hz':'006', '85.4 Hz':'007', '88.5 Hz':'008', |
|
78 |
+ '91.5 Hz':'009', '94.8 Hz':'010', '97.4 Hz':'011', |
|
79 |
+ '100.0 Hz':'012', '103.5 Hz':'013', '107.2 Hz':'014', |
|
80 |
+ '110.9 Hz':'015', '114.8 Hz':'016', '118.8 Hz':'017', |
|
81 |
+ '123.0 Hz':'018', '127.3 Hz':'019', '131.8 Hz':'020', |
|
82 |
+ '136.5 Hz':'021', '141.3 Hz':'022', '146.2 Hz':'023', |
|
83 |
+ '151.4 Hz':'024', '156.7 Hz':'025', '159.8 Hz':'026', |
|
84 |
+ '162.2 Hz':'027', '165.5 Hz':'028', '167.9 Hz':'029', |
|
85 |
+ '171.3 Hz':'030', '173.8 Hz':'031', '177.3 Hz':'032', |
|
86 |
+ '179.9 Hz':'033', '183.5 Hz':'034', '186.2 Hz':'035', |
|
87 |
+ '189.9 Hz':'036', '192.8 Hz':'037', '196.6 Hz':'038', |
|
88 |
+ '199.5 Hz':'039', '203.5 Hz':'040', '206.5 Hz':'041', |
|
89 |
+ '210.7 Hz':'042', '218.1 Hz':'043', '225.7 Hz':'044', |
|
90 |
+ '229.1 Hz':'045', '233.6 Hz':'046', '241.8 Hz':'047', |
|
91 |
+ '250.3 Hz':'048', '254.1 Hz':'049' } |
|
92 |
+ |
|
93 |
+# DCS Tones |
|
94 |
+dDcs = { '23':'000', '25':'001', '26':'002', '31':'003', '32':'004', |
|
95 |
+ '36':'005', '43':'006', '47':'007', '51':'008', '53':'009', |
|
96 |
+ '54':'010', '65':'011', '71':'012', '72':'013', '73':'014', |
|
97 |
+ '74':'015', '114':'016', '115':'017', '116':'018', '122':'019', |
|
98 |
+ '125':'020', '131':'021', '132':'022', '134':'023', '143':'024', |
|
99 |
+ '145':'025', '152':'026', '155':'027', '156':'028', '162':'029', |
|
100 |
+ '165':'030', '172':'031', '174':'032', '205':'033', '212':'034', |
|
101 |
+ '223':'035', '225':'036', '226':'037', '243':'038', '244':'039', |
|
102 |
+ '245':'040', '246':'041', '251':'042', '252':'043', '255':'044', |
|
103 |
+ '261':'045', '263':'046', '265':'047', '266':'048', '271':'049', |
|
104 |
+ '274':'050', '306':'051', '311':'052', '315':'053', '325':'054', |
|
105 |
+ '331':'055', '332':'056', '343':'057', '346':'058', '351':'059', |
|
106 |
+ '356':'060', '364':'061', '365':'062', '371':'063', '411':'064', |
|
107 |
+ '412':'065', '413':'066', '423':'067', '431':'068', '432':'069', |
|
108 |
+ '445':'070', '446':'071', '452':'072', '454':'073', '455':'074', |
|
109 |
+ '462':'075', '464':'076', '465':'077', '466':'078', '503':'079', |
|
110 |
+ '506':'080', '516':'081', '523':'082', '526':'083', '532':'084', |
|
111 |
+ '546':'085', '565':'086', '606':'087', '612':'088', '624':'089', |
|
112 |
+ '627':'090', '631':'091', '632':'092', '654':'093', '662':'094', |
|
113 |
+ '664':'095', '703':'096', '712':'097', '723':'098', '731':'099', |
|
114 |
+ '732':'100', '734':'101', '743':'102', '754':'103' } |
|
115 |
+ |
|
116 |
+# Clarifier state |
|
117 |
+dRxClar = { 'OFF':'0', 'ON':'1' } |
|
118 |
+dTxClar = { 'OFF':'0', 'ON':'1' } |
|
119 |
+ |
|
120 |
+# Define 'set' functions to encapsulate the various FT991 CAT commands. |
|
121 |
+ |
|
122 |
+def setMemory(dMem): |
|
123 |
+ """ |
|
124 |
+ Description: Returns a formatted MT command to the calling function. |
|
125 |
+ Parameters: dMem - a dictionary objected with the following keys |
|
126 |
+ defined: |
|
127 |
+ |
|
128 |
+ memloc - the memory location to be written |
|
129 |
+ rxfreq - the receive frequency of VFO-A in MHz |
|
130 |
+ mode - the modulation mode |
|
131 |
+ encode - the tone or DCS encoding mode |
|
132 |
+ shift - the direction of the repeater shift |
|
133 |
+ tag - a label for the memory location |
|
134 |
+ |
|
135 |
+ Returns: a string containing the formatted command |
|
136 |
+ """ |
|
137 |
+ sCmd = 'MC%0.3d;' % int(dMem['memloc']) |
|
138 |
+ sResult = sendCommand(sCmd) |
|
139 |
+ |
|
140 |
+ # While the 'MW' and 'MT' commands can be used to turn the Rx |
|
141 |
+ # and Tx clarifiers on, the clarifier states can only be turned |
|
142 |
+ # off by sending the 'RT0' and 'XT0' commands. This situation is |
|
143 |
+ # probably due to a potential bug in the CAT interface. |
|
144 |
+ sResult = sendCommand('RC;RT0;XT0;') |
|
145 |
+ |
|
146 |
+ sCmd = 'MT%0.3d' % int(dMem['memloc']) |
|
147 |
+ sCmd += '%d' % int(float(dMem['rxfreq']) * 1E6) |
|
148 |
+ sCmd += '%+0.4d' % int(dMem['clarfreq']) |
|
149 |
+ sCmd += dRxClar[dMem['rxclar']] |
|
150 |
+ sCmd += dTxClar[dMem['txclar']] |
|
151 |
+ sCmd += dMode[dMem['mode']] |
|
152 |
+ sCmd += '0' |
|
153 |
+ sCmd += dEncode[dMem['encode']] |
|
154 |
+ sCmd += '00' |
|
155 |
+ sCmd += dShift[dMem['shift']] |
|
156 |
+ sCmd += '0' |
|
157 |
+ sCmd += '%-12s' % dMem['tag'] |
|
158 |
+ sCmd += ';' |
|
159 |
+ sResult = sendCommand(sCmd) |
|
160 |
+ return sResult |
|
161 |
+## end def |
|
162 |
+ |
|
163 |
+def getMemory(memLoc): |
|
164 |
+ """ |
|
165 |
+ Description: |
|
166 |
+ Parameters: |
|
167 |
+ Returns: |
|
168 |
+ """ |
|
169 |
+ dMem = {} |
|
170 |
+ |
|
171 |
+ # Set memory location pointer in FT991. This is done |
|
172 |
+ # by sending the memory location select (MC) command. |
|
173 |
+ sCmd = 'MC%0.3d;' % (memLoc) |
|
174 |
+ sResult = sendCommand(sCmd) |
|
175 |
+ # Skip blank memory locations, which return '?;'. |
|
176 |
+ if sResult == '?;': |
|
177 |
+ return None |
|
178 |
+ |
|
179 |
+ # Send the get memory settings string to the FT991. |
|
180 |
+ sCmd = 'MT%0.3d;' % (memLoc) |
|
181 |
+ sResult = sendCommand(sCmd) |
|
182 |
+ |
|
183 |
+ # Parse memory settings string returned by the FT991 |
|
184 |
+ memloc = sResult[2:5] |
|
185 |
+ rxfreq = sResult[5:14] |
|
186 |
+ clarfreq = sResult[14:19] |
|
187 |
+ rxclar = sResult[19] |
|
188 |
+ txclar = sResult[20] |
|
189 |
+ mode = sResult[21] |
|
190 |
+ encode = sResult[23] |
|
191 |
+ shift = sResult[26] |
|
192 |
+ tag = sResult[28:40] |
|
193 |
+ |
|
194 |
+ # Store the memory settings in a dictionary object. |
|
195 |
+ dMem['memloc'] = str(int(memloc)) |
|
196 |
+ dMem['rxfreq'] = str(float(rxfreq) / 10**6) |
|
197 |
+ dMem['clarfreq'] = str(int(clarfreq)) |
|
198 |
+ dMem['rxclar'] = dRxClar.keys()[dRxClar.values().index(rxclar)] |
|
199 |
+ dMem['txclar'] = dTxClar.keys()[dTxClar.values().index(txclar)] |
|
200 |
+ dMem['mode'] = dMode.keys()[dMode.values().index(mode)] |
|
201 |
+ dMem['encode'] = dEncode.keys()[dEncode.values().index(encode)] |
|
202 |
+ dMem['shift'] = dShift.keys()[dShift.values().index(shift)] |
|
203 |
+ dMem['tag'] = tag.strip() |
|
204 |
+ |
|
205 |
+ return dMem |
|
206 |
+## end def |
|
207 |
+ |
|
208 |
+def getCTCSS(): |
|
209 |
+ """ |
|
210 |
+ Description: |
|
211 |
+ Parameters: |
|
212 |
+ Returns: |
|
213 |
+ """ |
|
214 |
+ # Get result CTCSS tone |
|
215 |
+ sResult = sendCommand('CN00;') |
|
216 |
+ tone = sResult[4:7] |
|
217 |
+ return dTones.keys()[dTones.values().index(tone)] |
|
218 |
+## end def |
|
219 |
+ |
|
220 |
+def getDCS(): |
|
221 |
+ """ |
|
222 |
+ Description: |
|
223 |
+ Parameters: |
|
224 |
+ Returns: |
|
225 |
+ """ |
|
226 |
+ # Get result of CN01 command |
|
227 |
+ sResult = sendCommand('CN01;') |
|
228 |
+ dcs = sResult[4:7] |
|
229 |
+ return dDcs.keys()[dDcs.values().index(dcs)] |
|
230 |
+## end def |
|
231 |
+ |
|
232 |
+def setCTCSS(tone): |
|
233 |
+ """ |
|
234 |
+ Description: returns a formatted CN command that sets the desired |
|
235 |
+ CTCSS tone. |
|
236 |
+ Parameters: tone - a string containing the CTCSS tone in Hz, e.g., |
|
237 |
+ '100 Hz' |
|
238 |
+ Returns: a string containing the formatted command |
|
239 |
+ """ |
|
240 |
+ sCmd = 'CN00%s;' % dTones[tone] |
|
241 |
+ return sendCommand(sCmd) |
|
242 |
+## end def |
|
243 |
+ |
|
244 |
+def setDCS(code): |
|
245 |
+ """ |
|
246 |
+ Description: returns a formatted CN command that sets the desired |
|
247 |
+ DCS code. |
|
248 |
+ Parameters: code - a string containing the DCS code, e.g., '23' |
|
249 |
+ Returns: a string containing the formatted command |
|
250 |
+ """ |
|
251 |
+ sCmd = 'CN01%s;' % dDcs[code] |
|
252 |
+ return sendCommand(sCmd) |
|
253 |
+## end def |
|
254 |
+ |
|
255 |
+def setPower(power): |
|
256 |
+ """ |
|
257 |
+ Description: returns a formatted PC command that sets the desired |
|
258 |
+ RF transmit power level. |
|
259 |
+ Parameters: power - Watts, an integer between 5 and 100 |
|
260 |
+ Returns: a string containing the formatted command |
|
261 |
+ """ |
|
262 |
+ sCmd = 'PC' |
|
263 |
+ sCmd += '%03.d;' % power |
|
264 |
+ return sendCommand(sCmd) |
|
265 |
+## end def |
|
266 |
+ |
|
267 |
+def parseCsvData(sline): |
|
268 |
+ """ |
|
269 |
+ Description: stores each item in the comma delimited line in a single |
|
270 |
+ dictionary object using a key appropriate for that item. |
|
271 |
+ Parameters: a string containing the comma delimited items to be parsed. |
|
272 |
+ Returns: a dictionary object containing the parsed line. |
|
273 |
+ """ |
|
274 |
+ dChan = {} # define an empty dictionary object |
|
275 |
+ lchan = sline.split(',') # split the line at the commas |
|
276 |
+ # If the first line is a header line, ignore it. |
|
277 |
+ if not lchan[0].isdigit(): |
|
278 |
+ return None |
|
279 |
+ # Store the parsed items with the appropriate key in the dictionary object. |
|
280 |
+ dChan['memloc'] = lchan[0] |
|
281 |
+ dChan['rxfreq'] = lchan[1] |
|
282 |
+ dChan['txfreq'] = lchan[2] |
|
283 |
+ dChan['offset'] = lchan[3] |
|
284 |
+ dChan['shift'] = lchan[4] |
|
285 |
+ dChan['mode'] = lchan[5] |
|
286 |
+ dChan['tag'] = lchan[6] |
|
287 |
+ dChan['encode'] = lchan[7] |
|
288 |
+ dChan['tone'] = lchan[8] |
|
289 |
+ dChan['dcs'] = str(int(lchan[9])) |
|
290 |
+ dChan['clarfreq'] = lchan[10] |
|
291 |
+ dChan['rxclar'] = lchan[11] |
|
292 |
+ dChan['txclar'] = lchan[12] |
|
293 |
+ return dChan # return the dictionary object |
|
294 |
+## end def |
|
295 |
+ |
|
296 |
+# Define serial communications functions. |
|
297 |
+ |
|
298 |
+def begin(baud=9600): |
|
299 |
+ """ |
|
300 |
+ Description: Initiates a serial connection the the FT991. Should |
|
301 |
+ always be called before sending commands to or |
|
302 |
+ receiving data from the FT991. Only needs to be called |
|
303 |
+ once. |
|
304 |
+ Parameters: none |
|
305 |
+ Returns: a pointer to the FT991 serial connection |
|
306 |
+ """ |
|
307 |
+ global ptrDevice |
|
308 |
+ |
|
309 |
+ # Determine OS type and set device port accordingly. |
|
310 |
+ OS_type = sys.platform |
|
311 |
+ if 'WIN' in OS_type.upper(): |
|
312 |
+ port = 'COM5' |
|
313 |
+ else: |
|
314 |
+ port = '/dev/ttyUSB0' |
|
315 |
+ |
|
316 |
+ # In debug mode do not actually send commands to the FT991. |
|
317 |
+ if debug: |
|
318 |
+ return |
|
319 |
+ # Create a FT991 object for serial communication |
|
320 |
+ try: |
|
321 |
+ ptrDevice = serial.Serial(port, baud, |
|
322 |
+ timeout=_INTERFACE_TIMEOUT) |
|
323 |
+ except Exception, error: |
|
324 |
+ if str(error).find('could not open port') > -1: |
|
325 |
+ print 'Please be sure the usb cable is properly connected to\n' \ |
|
326 |
+ 'your FT991 and to your computer, and that the FT991 is\n' \ |
|
327 |
+ 'turned ON. Then restart this program.' |
|
328 |
+ else: |
|
329 |
+ print 'Serial port error: %s\n' % error |
|
330 |
+ exit(1) |
|
331 |
+ time.sleep(.1) # give the connection a moment to settle |
|
332 |
+ return ptrDevice |
|
333 |
+## end def |
|
334 |
+ |
|
335 |
+def sendCommand(sCmd): |
|
336 |
+ """ |
|
337 |
+ Description: Sends a formatted FT911 command to the communication |
|
338 |
+ port connected to the FT991. Prints to stdout the |
|
339 |
+ answer from the FT991 (if any). |
|
340 |
+ Parameters: device - a pointer to the FT991 comm port |
|
341 |
+ sCmd - a string containing the formatted command |
|
342 |
+ Returns: nothing |
|
343 |
+ """ |
|
344 |
+ # Debug mode in conjunction with verbose mode is for verifying |
|
345 |
+ # correct formatting of commands before they are actually sent |
|
346 |
+ # to the FT991. |
|
347 |
+ if verbose: |
|
348 |
+ print sCmd, |
|
349 |
+ # In debug mode do not actually send commands to the FT991. |
|
350 |
+ if debug: |
|
351 |
+ return '' |
|
352 |
+ |
|
353 |
+ # Send the formatted command to the FT991 and get an answer, if any. |
|
354 |
+ # If the command does not generate an answer, no characters will be |
|
355 |
+ # returned by the FT991, resulting in an empty string returned by |
|
356 |
+ # the receiveSerial function. |
|
357 |
+ sendSerial(sCmd) |
|
358 |
+ sResult = receiveSerial(); |
|
359 |
+ if verbose: |
|
360 |
+ print sResult |
|
361 |
+ return sResult |
|
362 |
+## end def |
|
363 |
+ |
|
364 |
+def receiveSerial(termchar=';'): |
|
365 |
+ """ |
|
366 |
+ Description: Reads output one character at a time from the device |
|
367 |
+ until a terminating character is received. Returns a |
|
368 |
+ string containing the characters read from the serial |
|
369 |
+ port. |
|
370 |
+ Parameters: termchar - character terminating the answer string |
|
371 |
+ Returns: a string containing the received data |
|
372 |
+ """ |
|
373 |
+ answer = '' # initialize answer string to empty string |
|
374 |
+ charCount = 0 # reset read character count to zero |
|
375 |
+ |
|
376 |
+ while True: |
|
377 |
+ startTime = time.time() # Start read character timer |
|
378 |
+ c ='' |
|
379 |
+ while True: |
|
380 |
+ # Check for a character available in the serial read buffer. |
|
381 |
+ if ptrDevice.in_waiting: |
|
382 |
+ c = ptrDevice.read() |
|
383 |
+ break |
|
384 |
+ # Timeout if a character does not become available. |
|
385 |
+ if time.time() - startTime > _SERIAL_READ_TIMEOUT: |
|
386 |
+ break # Character waiting timer has timed out. |
|
387 |
+ # Return empty string if a character has not become available. |
|
388 |
+ if c == '': |
|
389 |
+ break; |
|
390 |
+ answer += c # Form a string from the received characters. |
|
391 |
+ charCount += 1 # Increment character count. |
|
392 |
+ # If a semicolon has arrived then the FT991 has completed |
|
393 |
+ # sending output to the serial port so stop reading characters. |
|
394 |
+ # Also stop if max characters received. |
|
395 |
+ if c == termchar: |
|
396 |
+ break |
|
397 |
+ if charCount > _SERIAL_READ_BUFFER_LENGTH: |
|
398 |
+ raise Exception('serial read buffer overflow') |
|
399 |
+ ptrDevice.flushInput() # Flush serial buffer to prevent overflows. |
|
400 |
+ return answer |
|
401 |
+## end def |
|
402 |
+ |
|
403 |
+def sendSerial(command): |
|
404 |
+ """ |
|
405 |
+ Description: Writes a string to the device. |
|
406 |
+ Parameters: command - string containing the FT991 command |
|
407 |
+ Returns: nothing |
|
408 |
+ """ |
|
409 |
+ # In debug we only want to see the output of the command formatter, |
|
410 |
+ # not actually send commands to the FT991. Debug mode should be |
|
411 |
+ # used in conjunction with verbose mode. |
|
412 |
+ ptrDevice.write(command) # Send command string to FT991 |
|
413 |
+ ptrDevice.flushOutput() # Flush serial buffer to prevent overflows |
|
414 |
+## end def |
|
415 |
+ |
|
416 |
+def main(): |
|
417 |
+ """ |
|
418 |
+ Description: Place code for testing this module here. |
|
419 |
+ Parameters: none |
|
420 |
+ Returns: nothing |
|
421 |
+ """ |
|
422 |
+ # Test this module. |
|
423 |
+ global verbose, debug |
|
424 |
+ |
|
425 |
+ verbose = True |
|
426 |
+ debug = False |
|
427 |
+ |
|
428 |
+ begin() |
|
429 |
+ sendCommand('IF;') |
|
430 |
+ sendCommand('MC001;') |
|
431 |
+ sendCommand('ZZZ;') |
|
432 |
+ |
|
433 |
+ dMem = {'rxfreq': '146.52', 'shift': 'OFF', 'encode': 'OFF', \ |
|
434 |
+ 'txclar': 'OFF', 'tag': 'KA7JLO', 'mode': 'FM', 'rxclar': 'OFF', \ |
|
435 |
+ 'memloc': '99', 'clarfreq': '0'} |
|
436 |
+ setMemory(dMem) |
|
437 |
+ print getMemory(99) |
|
438 |
+ |
|
439 |
+## end def |
|
440 |
+ |
|
441 |
+if __name__ == '__main__': |
|
442 |
+ main() |
0 | 443 |
new file mode 100755 |
... | ... |
@@ -0,0 +1,517 @@ |
1 |
+#!/usr/bin/python -u |
|
2 |
+# The -u option turns off block buffering of python output. This assures |
|
3 |
+# that output streams to stdout when output happens. |
|
4 |
+# |
|
5 |
+# Module: ft991utility.py |
|
6 |
+# |
|
7 |
+# Description: A utility for backing up Yaesu FT991 memory and menu settings, |
|
8 |
+# and also for restoring memory and menu settings from |
|
9 |
+# a file. Can be used both interactively or with command line |
|
10 |
+# arguments. Before running this utility, be sure to copy |
|
11 |
+# the file 'ft911.py' to the same folder as this utility. |
|
12 |
+# |
|
13 |
+# Copyright 2019 by Jeff Owrey, Intravisions.com |
|
14 |
+# This program is free software: you can redistribute it and/or modify |
|
15 |
+# it under the terms of the GNU General Public License as published by |
|
16 |
+# the Free Software Foundation, either version 3 of the License, or |
|
17 |
+# (at your option) any later version. |
|
18 |
+# |
|
19 |
+# This program is distributed in the hope that it will be useful, |
|
20 |
+# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
21 |
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
22 |
+# GNU General Public License for more details. |
|
23 |
+# |
|
24 |
+# You should have received a copy of the GNU General Public Licensef |
|
25 |
+# along with this program. If not, see http://www.gnu.org/license. |
|
26 |
+# |
|
27 |
+# Revision History |
|
28 |
+# * v10 23 Nov 2019 by J L Owrey; first release |
|
29 |
+# |
|
30 |
+# This script has been tested with the following |
|
31 |
+# |
|
32 |
+# Python 2.7.15rc1 (default, Nov 12 2018, 14:31:15) |
|
33 |
+# [GCC 7.3.0] on linux2 |
|
34 |
+#2345678901234567890123456789012345678901234567890123456789012345678901234567890 |
|
35 |
+ |
|
36 |
+import os, sys, serial, time |
|
37 |
+import ft991 # module should be in same directory as this utility |
|
38 |
+ |
|
39 |
+# Constant definitions |
|
40 |
+ |
|
41 |
+_DEFAULT_MENU_SETTINGS_FILE = 'ft991menu.cfg' |
|
42 |
+_DEFAULT_MEMORY_SETTINGS_FILE = 'ft991mem.csv' |
|
43 |
+_MAX_NUMBER_OF_MENU_ITEMS = 154 |
|
44 |
+_MAX_NUMBER_OF_MEMORY_ITEMS = 118 |
|
45 |
+ |
|
46 |
+# Global definitions |
|
47 |
+ |
|
48 |
+menuBackupFile = _DEFAULT_MENU_SETTINGS_FILE |
|
49 |
+memoryBackupFile = _DEFAULT_MEMORY_SETTINGS_FILE |
|
50 |
+commandLineOption = '' |
|
51 |
+ |
|
52 |
+# Command processing functions |
|
53 |
+ |
|
54 |
+def doUserCommand(): |
|
55 |
+ """ |
|
56 |
+ Description: Provides an interactive user interface where the user can |
|
57 |
+ enter simple text commands at a command line prompt. The |
|
58 |
+ commands that a user can enter are listed in a menu splash |
|
59 |
+ that the user can display anytime with the 'm' command. |
|
60 |
+ Also this function processes commands provided as command |
|
61 |
+ line options. In the case of a command line option this |
|
62 |
+ function operates non-interactively. |
|
63 |
+ Parameters: none |
|
64 |
+ Returns: nothing |
|
65 |
+ """ |
|
66 |
+ # When command line arguments have not been provided, |
|
67 |
+ # use interactive mode and give the user a prompt. |
|
68 |
+ if commandLineOption == '': |
|
69 |
+ cmd = raw_input('>').strip() |
|
70 |
+ # If command line arguments have been provided, then |
|
71 |
+ # execute the command non-interactively. |
|
72 |
+ else: |
|
73 |
+ cmd = commandLineOption |
|
74 |
+ |
|
75 |
+ # Process the user command. |
|
76 |
+ if cmd == '': |
|
77 |
+ return |
|
78 |
+ elif cmd == 'm': |
|
79 |
+ printMenuSplash() |
|
80 |
+ elif cmd == 'bu': |
|
81 |
+ backupMenuSettings() |
|
82 |
+ elif cmd == 'ru': |
|
83 |
+ restoreMenuSettings() |
|
84 |
+ elif cmd == 'bm': |
|
85 |
+ backupMemorySettings() |
|
86 |
+ elif cmd == 'rm': |
|
87 |
+ restoreMemorySettings() |
|
88 |
+ elif cmd == 'p': |
|
89 |
+ passThroughMode() |
|
90 |
+ elif cmd == 'v': |
|
91 |
+ toggleVerboseMode() |
|
92 |
+ elif cmd == 'x': |
|
93 |
+ exit(0) |
|
94 |
+ else: |
|
95 |
+ print "invalid command" |
|
96 |
+## end def |
|
97 |
+ |
|
98 |
+def backupMemorySettings(): |
|
99 |
+ """ |
|
100 |
+ Description: Backs up all memory settings to a file. The user has the |
|
101 |
+ option of providing a file name or accepting a default |
|
102 |
+ file name. |
|
103 |
+ Parameters: none |
|
104 |
+ Returns: nothing |
|
105 |
+ """ |
|
106 |
+ # Prompt the user for a file name in which to store |
|
107 |
+ # backed up memory settings. |
|
108 |
+ fileName = getFileName(memoryBackupFile) |
|
109 |
+ # Read the memory settings from the FT991... |
|
110 |
+ print 'Backing up memory settings...' |
|
111 |
+ settings = readMemorySettings() |
|
112 |
+ # and write them to the file. |
|
113 |
+ writeToFile(settings, fileName) |
|
114 |
+ print 'Memory settings backed up to \'%s\'' % fileName |
|
115 |
+## end def |
|
116 |
+ |
|
117 |
+def restoreMemorySettings(): |
|
118 |
+ """ |
|
119 |
+ Description: Restores all memory settings from a file. The user has the |
|
120 |
+ option of providing a file name or accepting a default |
|
121 |
+ file name. |
|
122 |
+ Parameters: none |
|
123 |
+ Returns: nothing |
|
124 |
+ """ |
|
125 |
+ # Prompt the user for a file name from which to retrieve backed up |
|
126 |
+ # memory settings. Also make sure the file exists. |
|
127 |
+ fileName = getFileName(memoryBackupFile) |
|
128 |
+ if not os.path.isfile(fileName): |
|
129 |
+ print 'File not found.\n' \ |
|
130 |
+ 'Please enter a valid file name. Be sure to correctly ' \ |
|
131 |
+ 'enter\nthe full path name or relative path name of the file.' |
|
132 |
+ return |
|
133 |
+ # Read the memory settings from the file... |
|
134 |
+ print 'Restoring memory settings...' |
|
135 |
+ settings = readFromFile(fileName) |
|
136 |
+ # and write them to the FT991. |
|
137 |
+ writeMemorySettings(settings) |
|
138 |
+ print 'Memory settings restored from \'%s\'' % fileName |
|
139 |
+## end def |
|
140 |
+ |
|
141 |
+def backupMenuSettings(): |
|
142 |
+ """ |
|
143 |
+ Description: Backs up all menu settings to a file. The user has the |
|
144 |
+ option of providing a file name or accepting a default |
|
145 |
+ file name. |
|
146 |
+ Parameters: none |
|
147 |
+ Returns: nothing |
|
148 |
+ """ |
|
149 |
+ # Prompt the user for a file name in which to store |
|
150 |
+ # backed up menu settings. |
|
151 |
+ fileName = getFileName(menuBackupFile) |
|
152 |
+ # Read the menu settings from the FT991... |
|
153 |
+ print 'Backing up menu settings...' |
|
154 |
+ settings = readMenuSettings() |
|
155 |
+ # and write them to the file. |
|
156 |
+ writeToFile(settings, fileName) |
|
157 |
+ print 'Menu settings backed up to \'%s\'' % fileName |
|
158 |
+## end def |
|
159 |
+ |
|
160 |
+def restoreMenuSettings(): |
|
161 |
+ """ |
|
162 |
+ Description: Restores all menu settings from a file. The user has the |
|
163 |
+ option of providing a file name or accepting a default |
|
164 |
+ file name. |
|
165 |
+ Parameters: none |
|
166 |
+ Returns: nothing |
|
167 |
+ """ |
|
168 |
+ # Prompt the user for a file name from which to retrieve backed up |
|
169 |
+ # menu settings. Also make sure the file exists. |
|
170 |
+ fileName = getFileName(menuBackupFile) |
|
171 |
+ if not os.path.isfile(fileName): |
|
172 |
+ print 'File not found.\n' \ |
|
173 |
+ 'Please enter a valid file name. Be sure to correctly ' \ |
|
174 |
+ 'enter\nthe full path name or relative path name of the file.' |
|
175 |
+ return |
|
176 |
+ # Read the menu settings from the file... |
|
177 |
+ print 'Restoring menu settings...' |
|
178 |
+ settings = readFromFile(fileName) |
|
179 |
+ # and write them to the FT991. |
|
180 |
+ writeMenuSettings(settings) |
|
181 |
+ print 'Menu settings restored from \'%s\'' % fileName |
|
182 |
+## end def |
|
183 |
+ |
|
184 |
+def passThroughMode(): |
|
185 |
+ """ |
|
186 |
+ Description: An interactive mode whereby the user an enter FT991 CAT |
|
187 |
+ commands directly on the command line. This mode greatly |
|
188 |
+ facilitates development and debugging. |
|
189 |
+ Parameters: none |
|
190 |
+ Returns: nothing |
|
191 |
+ """ |
|
192 |
+ print 'Entering passthrough mode. Type \'exit\' to exit mode.' |
|
193 |
+ while(True): |
|
194 |
+ # Prompt the user to enter an FT991 CAT command, and |
|
195 |
+ # process the command string. |
|
196 |
+ sCommand = raw_input('CAT# ').upper() |
|
197 |
+ if sCommand == 'EXIT': # exit this utility |
|
198 |
+ break |
|
199 |
+ # If the user fails to end a CAT command with a semi-colon, |
|
200 |
+ # then provide one. |
|
201 |
+ elif sCommand[-1:] != ';': |
|
202 |
+ sCommand += ';' |
|
203 |
+ |
|
204 |
+ if sCommand == '': # no command - do nothing |
|
205 |
+ continue |
|
206 |
+ else: # run a user command |
|
207 |
+ ft991.sendSerial(sCommand) |
|
208 |
+ sResult = ft991.receiveSerial(); |
|
209 |
+ if sResult != '': |
|
210 |
+ print sResult |
|
211 |
+## end def |
|
212 |
+ |
|
213 |
+def toggleVerboseMode(): |
|
214 |
+ """ |
|
215 |
+ Description: Toggles the verbose mode on or off, depending on the |
|
216 |
+ previous state. Verbose mode causes CAT commands to be |
|
217 |
+ echoed to STDOUT as they are sent to the FT991. If a |
|
218 |
+ CAT command returns a string, that is also echoed. |
|
219 |
+ Parameters: none |
|
220 |
+ Returns: nothing |
|
221 |
+ """ |
|
222 |
+ if ft991.verbose: |
|
223 |
+ ft991.verbose = False |
|
224 |
+ print 'Verbose is OFF' |
|
225 |
+ else: |
|
226 |
+ ft991.verbose = True |
|
227 |
+ print 'Verbose is ON' |
|
228 |
+## end def |
|
229 |
+ |
|
230 |
+def getFileName(defaultFile): |
|
231 |
+ """ |
|
232 |
+ Description: Prompts the user for a file name. |
|
233 |
+ Parameters: defaultFile - file name to use if the user does not |
|
234 |
+ provide a file name |
|
235 |
+ Returns: the user provided file name if provided, or the default |
|
236 |
+ file name, otherwise. |
|
237 |
+ """ |
|
238 |
+ # If a command backup or restore argument provided, then do not |
|
239 |
+ # query the user for a file name. A file name may be provided |
|
240 |
+ # as an option on the command line. |
|
241 |
+ if commandLineOption != '': |
|
242 |
+ return defaultFile |
|
243 |
+ # Otherwise query the user for a file name |
|
244 |
+ fileName = raw_input("Enter file name or <CR> for default: ") |
|
245 |
+ if fileName == '': |
|
246 |
+ return defaultFile |
|
247 |
+ else: |
|
248 |
+ return fileName |
|
249 |
+## end def |
|
250 |
+ |
|
251 |
+def readMenuSettings(): |
|
252 |
+ """ |
|
253 |
+ Description: Reads all menu settings from the FT991. |
|
254 |
+ Parameters: none |
|
255 |
+ Returns: a list object containing all the menu settings |
|
256 |
+ """ |
|
257 |
+ lMenuSettings = [] |
|
258 |
+ # Iterate through all menu items, getting each setting and storing |
|
259 |
+ # the setting in a file. |
|
260 |
+ for inx in range(1, _MAX_NUMBER_OF_MENU_ITEMS): |
|
261 |
+ # Format the read menu item CAT command. |
|
262 |
+ sCommand = 'EX%0.3d;' % inx |
|
263 |
+ # Send the command to the FT991. |
|
264 |
+ sResult = ft991.sendCommand(sCommand) |
|
265 |
+ # Add the menu setting to a list object. |
|
266 |
+ lMenuSettings.append(sResult) |
|
267 |
+ return lMenuSettings |
|
268 |
+## end def |
|
269 |
+ |
|
270 |
+def writeMenuSettings(lMenuSettings): |
|
271 |
+ """ |
|
272 |
+ Description: Writes supplied menu settings to the FT991. |
|
273 |
+ Parameters: lMenuSettings - a list object containing menu settings |
|
274 |
+ Returns: nothing |
|
275 |
+ """ |
|
276 |
+ for item in lMenuSettings: |
|
277 |
+ |
|
278 |
+ # Do not write read-only menu settings as this results |
|
279 |
+ # in the FT-991 returning an error. The only read-only |
|
280 |
+ # setting is the "Radio ID" setting. |
|
281 |
+ if item.find('EX087') > -1: |
|
282 |
+ continue; |
|
283 |
+ # Send the pre-formatted menu setting to the FT991. |
|
284 |
+ sResult = ft991.sendCommand(item) |
|
285 |
+ if sResult.find('?;') > -1: |
|
286 |
+ print 'error restoring menu setting: %s' % item |
|
287 |
+ exit(1) |
|
288 |
+## end def |
|
289 |
+ |
|
290 |
+def readMemorySettings(): |
|
291 |
+ """ |
|
292 |
+ Description: Reads all defined memory settings from the FT991. The |
|
293 |
+ settings are reformatted into a readable, comma-delimited |
|
294 |
+ (csv) file, which can be viewed and modified with a |
|
295 |
+ spreadsheet application such as LibreOffice Calc. |
|
296 |
+ Parameters: none |
|
297 |
+ Returns: a list object containing memory location settings. Each item |
|
298 |
+ in the list represents a row, in comma-delimited form, that |
|
299 |
+ specifies the settings for a single memory location. The first |
|
300 |
+ item in the list are the column headers for remaining rows |
|
301 |
+ contained in the list. |
|
302 |
+ """ |
|
303 |
+ # Define the column headers as the first item in the list. |
|
304 |
+ lMemorySettings = [ 'Memory Ch,Rx Frequency,Tx Frequency,Offset,' \ |
|
305 |
+ 'Repeater Shift,Mode,Tag,Encoding,Tone,DCS,' \ |
|
306 |
+ 'Clarifier, RxClar, TxClar' ] |
|
307 |
+ |
|
308 |
+ for memoryLocation in range(1, _MAX_NUMBER_OF_MEMORY_ITEMS): |
|
309 |
+ # For each memory location get the memory contents. Note that |
|
310 |
+ # several CAT commands are required to get the entire contents |
|
311 |
+ # of a memory location. Specifically, additional commands are |
|
312 |
+ # required to get DCS code and CTCSS tone. |
|
313 |
+ dMem = ft991.getMemory(memoryLocation) |
|
314 |
+ # If a memory location is empty (has not been programmed or has |
|
315 |
+ # been erased), do not created a list entry for that location. |
|
316 |
+ if dMem == None: |
|
317 |
+ continue |
|
318 |
+ # Get DCS and CTCSS. |
|
319 |
+ tone = ft991.getCTCSS() |
|
320 |
+ dcs = ft991.getDCS() |
|
321 |
+ # getMemory, above, stores data in a dictionary object. Format |
|
322 |
+ # the data in this object, as well as, the DCS code and CTCSS |
|
323 |
+ # tone into a comma-delimited string. |
|
324 |
+ sCsvFormat = '%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,' % \ |
|
325 |
+ ( dMem['memloc'], dMem['rxfreq'], '', '', \ |
|
326 |
+ dMem['shift'], dMem['mode'], dMem['tag'], dMem['encode'], \ |
|
327 |
+ tone, dcs, dMem['clarfreq'], dMem['rxclar'], \ |
|
328 |
+ dMem['txclar'] ) |
|
329 |
+ # Add the comma-delimited string to the list object. |
|
330 |
+ lMemorySettings.append(sCsvFormat) |
|
331 |
+ if ft991.verbose: |
|
332 |
|
|
333 |
+ return lMemorySettings |
|
334 |
+# end def |
|
335 |
+ |
|
336 |
+def writeMemorySettings(lMemorySettings): |
|
337 |
+ """ |
|
338 |
+ Description: Writes the supplied memory settings to the FT991. |
|
339 |
+ Parameters: lMemorySettings - a list object containing the memory |
|
340 |
+ settings in comma delimited format |
|
341 |
+ Returns: nothing |
|
342 |
+ """ |
|
343 |
+ for item in lMemorySettings: |
|
344 |
+ # Parse the comma-delimited line and store in a dictionary object. |
|
345 |
+ dItem = ft991.parseCsvData(item) |
|
346 |
+ # The first item in the memory settings list are the column headers; |
|
347 |
+ # so ignore this item. (parseData returns None for this item.) |
|
348 |
+ if dItem == None: |
|
349 |
+ continue |
|
350 |
+ # Set memory channel vfo, mode, and other data. |
|
351 |
+ sResult = '' |
|
352 |
+ sResult += ft991.setMemory(dItem) |
|
353 |
+ # Set CTCSS tone for memory channel. |
|
354 |
+ sResult += ft991.setCTCSS(dItem['tone']) |
|
355 |
+ # Set DCS code for memory channel. |
|
356 |
+ sResult += ft991.setDCS(dItem['dcs']) |
|
357 |
+ # Process any errors returned by the CAT interface. |
|
358 |
+ if sResult.find('?;') > -1: |
|
359 |
+ print 'error restoring memory setting: %s' % sResult |
|
360 |
+ if ft991.verbose: |
|
361 |
|
|
362 |
+## end def |
|
363 |
+ |
|
364 |
+def writeToFile(lSettings, fileName): |
|
365 |
+ """ |
|
366 |
+ Description: Writes supplied settings to the specified file. |
|
367 |
+ Parameters: lSettings - a list object containing the settings |
|
368 |
+ fileName - the name of the output file |
|
369 |
+ Returns: nothing |
|
370 |
+ """ |
|
371 |
+ fout = open(fileName, 'w') |
|
372 |
+ for item in lSettings: |
|
373 |
+ fout.write('%s\n' % item) |
|
374 |
+ fout.close() |
|
375 |
+## end def |
|
376 |
+ |
|
377 |
+def readFromFile(fileName): |
|
378 |
+ """ |
|
379 |
+ Description: Reads settings from the specified file. |
|
380 |
+ Parameters: fileName - the name of the input file |
|
381 |
+ Returns: a list object containing the settings |
|
382 |
+ """ |
|
383 |
+ lSettings = [] |
|
384 |
+ |
|
385 |
+ fin = open(fileName, 'r') |
|
386 |
+ for line in fin: |
|
387 |
+ item = line.strip() # remove new line characters |
|
388 |
+ lSettings.append(item) |
|
389 |
+ fin.close() |
|
390 |
+ return lSettings |
|
391 |
+## end def |
|
392 |
+ |
|
393 |
+def setIOFile(option, commandLineFile): |
|
394 |
+ """ |
|
395 |
+ Description: Provides a connector between the command line and the |
|
396 |
+ interactive interface. Commands included as arguments |
|
397 |
+ on the command line determine default file names, as |
|
398 |
+ well as the command executed by doUserCommand above. |
|
399 |
+ Parameters: option - the command supplied by the command line |
|
400 |
+ argument interpreter getCLarguments. |
|
401 |
+ commandLineFile - the name of the file supplied by the |
|
402 |
+ -f command line argument (if supplied). |
|
403 |
+ Returns: nothing |
|
404 |
+ """ |
|
405 |
+ global menuBackupFile, memoryBackupFile, commandLineOption |
|
406 |
+ |
|
407 |
+ commandLineOption = option |
|
408 |
+ if commandLineFile == '': |
|
409 |
+ return |
|
410 |
+ if (option == 'bu' or option == 'ru'): |
|
411 |
+ menuBackupFile = commandLineFile # set menu backup file name |
|
412 |
+ elif (option == 'bm' or option == 'rm'): |
|
413 |
+ memoryBackupFile = commandLineFile # set memory backup file name |
|
414 |
+## end def |
|
415 |
+ |
|
416 |
+def printMenuSplash(): |
|
417 |
+ """ |
|
418 |
+ Description: Prints an menu of available commands for use in |
|
419 |
+ interactive mode. |
|
420 |
+ Parameters: none |
|
421 |
+ Returns: nothing |
|
422 |
+ """ |
|
423 |
+ splash = \ |
|
424 |
+""" |
|
425 |
+Enter menu item number. |
|
426 |
+m - show this menu |
|
427 |
+bm - backup memory to file |
|
428 |
+rm - restore memory from file |
|
429 |
+bu - backup menu to file |
|
430 |
+ru - restore menu from file |
|
431 |
+p - enter passthrough mode |
|
432 |
+v - toggle verbose mode |
|
433 |
+x - exit this program |
|
434 |
+""" |
|
435 |
+ print splash |
|
436 |
+## end def |
|
437 |
+ |
|
438 |
+def getCLarguments(): |
|
439 |
+ """ Description: gets command line arguments and configures this program |
|
440 |
+ to run accordingly. See the variable 'usage', below, |
|
441 |
+ for possible arguments that may be used on the command |
|
442 |
+ line. |
|
443 |
+ Parameters: none |
|
444 |
+ Returns: nothing |
|
445 |
+ """ |
|
446 |
+ index = 1 |
|
447 |
+ fileName = '' |
|
448 |
+ backupOption = '' |
|
449 |
+ |
|
450 |
+ # Define a splash to help the user enter command line arguments. |
|
451 |
+ usage = "Usage: %s [-v] [OPTION] [-f file]\n" \ |
|
452 |
+ " -b: backup memory\n" \ |
|
453 |
+ " -r: restore memory\n" \ |
|
454 |
+ " -m: backup menu\n" \ |
|
455 |
+ " -s: restore menu\n" \ |
|
456 |
+ " -f: backup/restore file name\n" \ |
|
457 |
+ " -v: verbose mode\n" \ |
|
458 |
+ % sys.argv[0].split('/')[-1] |
|
459 |
+ |
|
460 |
+ # Process all command line arguments until done. Note that the last |
|
461 |
+ # dash b, m, r, or s argument encounterd on the command line will be |
|
462 |
+ # the one actually execute; any previous instances will be ignored. |
|
463 |
+ while index < len(sys.argv): |
|
464 |
+ if sys.argv[index] == '-f': # Backup file provided. |
|
465 |
+ # Get the backup file name. |
|
466 |
+ if len(sys.argv) < index + 2: |
|
467 |
+ print "-f option requires file name" |
|
468 |
+ exit(1); |
|
469 |
+ fileName = sys.argv[index + 1] |
|
470 |
+ index += 1 |
|
471 |
+ elif sys.argv[index] == '-b': # backup memory |
|
472 |
+ backupOption = 'bm' |
|
473 |
+ elif sys.argv[index] == '-r': # restore memory |
|
474 |
+ backupOption = 'rm' |
|
475 |
+ elif sys.argv[index] == '-m': # backup menu |
|
476 |
+ backupOption = 'bu' |
|
477 |
+ elif sys.argv[index] == '-s': # restore menu |
|
478 |
+ backupOption = 'ru' |
|
479 |
+ elif sys.argv[index] == '-v': # set verbose mode 'ON' |
|
480 |
+ ft991.verbose = True |
|
481 |
+ elif sys.argv[index] == '-d': # set debug mode 'ON' |
|
482 |
+ ft991.debug = True |
|
483 |
+ else: |
|
484 |
+ print usage |
|
485 |
+ exit(-1) |
|
486 |
+ index += 1 |
|
487 |
+ ## end while |
|
488 |
+ |
|
489 |
+ # Set backup file name and backup command to execute. |
|
490 |
+ setIOFile(backupOption, fileName) |
|
491 |
+##end def |
|
492 |
+ |
|
493 |
+def main(): |
|
494 |
+ """ |
|
495 |
+ Description: Opens a com port connection to the FT991. Processes any |
|
496 |
+ supplied command line options. If no command line options |
|
497 |
+ provides, then enters interactive mode. |
|
498 |
+ Parameters: none |
|
499 |
+ Returns: nothing |
|
500 |
+ """ |
|
501 |
+ getCLarguments() # get command line options |
|
502 |
+ |
|
503 |
+ ft991.begin() # open com port session to FT991 |
|
504 |
+ |
|
505 |
+ # Process command line options (if any). |
|
506 |
+ if commandLineOption != '': |
|
507 |
+ doUserCommand() |
|
508 |
+ return |
|
509 |
+ |
|
510 |
+ # Else enter user interactive mode. |
|
511 |
+ printMenuSplash() |
|
512 |
+ while(1): |
|
513 |
+ doUserCommand() |
|
514 |
+## end def |
|
515 |
+ |
|
516 |
+if __name__ == '__main__': |
|
517 |
+ main() |