Browse code

reorganize repository

Gandolf authored on 11/25/2019 06:37:12
Showing 1 changed files
1 1
deleted file mode 100755
... ...
@@ -1,517 +0,0 @@
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
-            print
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
-            print
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()
Browse code

ft991 utility replaces writeMemory, and passThrough

Gandolf authored on 11/24/2019 23:01:28
Showing 1 changed files
1 1
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
+            print
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
+            print
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()