Browse code

all memory

gandolf authored on 02/01/2020 05:02:27
Showing 1 changed files
... ...
@@ -55,6 +55,9 @@ ptrDevice = None
55 55
 # CTCSS tone, and DCS code are translated to the repective FT991 parameter
56 56
 # value.
57 57
 
58
+# Binary On / Off state
59
+bState = { 'OFF':'0', 'ON':'1' }
60
+
58 61
 # Modulation modes
59 62
 dMode = { 'LSB':'1', 'USB':'2', 'CW':'3', 'FM':'4', 'AM':'5',
60 63
           'RTTY-LSB':'6', 'CW-R':'7', 'DATA-LSB':'8', 'RTTY-USB':'9',
... ...
@@ -113,9 +116,12 @@ dDcs = { '23':'000', '25':'001', '26':'002', '31':'003', '32':'004',
113 116
          '664':'095', '703':'096', '712':'097', '723':'098', '731':'099',
114 117
          '732':'100', '734':'101', '743':'102', '754':'103' }
115 118
 
116
-# Clarifier state
117
-dRxClar = { 'OFF':'0', 'ON':'1' }
118
-dTxClar = { 'OFF':'0', 'ON':'1' }
119
+# Preamplifier State
120
+dPreamp = { 'IPO':'0', 'AMP 1':'1', 'AMP 2':'2' }
121
+
122
+# Narror band filter state
123
+dNAR = { 'WIDE':'0', 'NARROW':'1' }
124
+
119 125
 
120 126
 #############################################################################
121 127
 # Define 'get' methods to encapsulate FT991 commands returning status info. #
... ...
@@ -135,24 +141,24 @@ def getMemory(memloc):
135 141
 
136 142
     # Parse memory settings string returned by the FT991
137 143
     memloc = sResult[2:5]
138
-    rxfreq = sResult[5:14]
144
+    vfoa = sResult[5:14]
139 145
     clarfreq = sResult[14:19]
140 146
     rxclar = sResult[19]
141 147
     txclar = sResult[20]
142 148
     mode = sResult[21]
143 149
     encode = sResult[23]
144
-    shift = sResult[26]
150
+    rpoffset = sResult[26]
145 151
     tag = sResult[28:40]
146 152
 
147 153
     # Store the memory settings in a dictionary object.
148 154
     dMem['memloc'] = str(int(memloc))
149
-    dMem['rxfreq'] = str(float(rxfreq) / 10**6)
155
+    dMem['vfoa'] = str(float(vfoa) / 10**6)
150 156
     dMem['clarfreq'] = str(int(clarfreq))
151
-    dMem['rxclar'] = dRxClar.keys()[dRxClar.values().index(rxclar)]
152
-    dMem['txclar'] = dTxClar.keys()[dTxClar.values().index(txclar)]
157
+    dMem['rxclar'] = bState.keys()[bState.values().index(rxclar)]
158
+    dMem['txclar'] = bState.keys()[bState.values().index(txclar)]
153 159
     dMem['mode'] = dMode.keys()[dMode.values().index(mode)]
154 160
     dMem['encode'] = dEncode.keys()[dEncode.values().index(encode)]
155
-    dMem['shift'] = dShift.keys()[dShift.values().index(shift)]
161
+    dMem['rpoffset'] = dShift.keys()[dShift.values().index(rpoffset)]
156 162
     dMem['tag'] = tag.strip()
157 163
 
158 164
     return dMem
... ...
@@ -190,10 +196,9 @@ def getRxClarifier():
190 196
     """
191 197
     # An exception will automatically be raised if incorrect data is
192 198
     # supplied - most likely a "key not found" error.
193
-    sCmd = 'RT;'
194
-    sResult = sendCommand(sCmd)
199
+    sResult = sendCommand('RT;')
195 200
     state = sResult[2]
196
-    return dRxClar.keys()[dRxClar.values().index(state)]
201
+    return bState.keys()[bState.values().index(state)]
197 202
 ## end def
198 203
 
199 204
 def getTxClarifier():
... ...
@@ -204,14 +209,182 @@ def getTxClarifier():
204 209
     """
205 210
     # An exception will automatically be raised if incorrect data is
206 211
     # supplied - most likely a "key not found" error.
207
-    sCmd = 'XT;'
208
-    sResult = sendCommand(sCmd)
212
+    sResult = sendCommand('XT;')
209 213
     state = sResult[2]
210
-    return dTxClar.keys()[dTxClar.values().index(state)]
214
+    return bState.keys()[bState.values().index(state)]
215
+## end def
216
+
217
+def getPower():
218
+    """
219
+    Description:  Gets the transmit power level.
220
+    Parameters: none
221
+    Returns: string containing the power in Watts
222
+    """
223
+    sResult = sendCommand('PC;')
224
+    return sResult[2:5]
225
+##end def
226
+
227
+def getPreamp():
228
+    """
229
+    Description:  Gets the state of the Rx preamplifier.
230
+    Parameters: none
231
+    Returns: string containing the state of the preamplifier.
232
+    """
233
+    # Get result of PA0 command
234
+    sResult = sendCommand('PA0;')
235
+    ipo = sResult[3:4]
236
+    return dPreamp.keys()[dPreamp.values().index(ipo)]
237
+## end def
238
+
239
+def getRfAttn():
240
+    """
241
+    Description:  Gets the state of the Rf attenuator.
242
+    Parameters: none
243
+    Returns: string containing the state of the Rf attenuator.
244
+    """
245
+    sResult = sendCommand('RA0;')
246
+    if sResult == '?;':
247
+        return 'NA'
248
+    attn = sResult[3:4]
249
+    return bState.keys()[bState.values().index(attn)]
250
+## end def
251
+
252
+def getNoiseBlanker():
253
+    """
254
+    Description:  Gets the state of the noise blanker.
255
+    Parameters: none
256
+    Returns: string containing the state of the noise blanker.
257
+    """
258
+    sResult = sendCommand('NB0;')
259
+    nb = sResult[3:4]
260
+    return bState.keys()[bState.values().index(nb)]
261
+## end def
262
+
263
+def getIFshift():
264
+    """
265
+    Description:  Gets the value in Hz of IF shift.
266
+    Parameters: none
267
+    Returns: string containing the amount of shift.
268
+    """
269
+    if getRfAttn() == 'NA':
270
+        return 'NA'
271
+    sResult = sendCommand('IS0;')
272
+    if sResult == '?;':
273
+        return 'NA'
274
+    shift = int(sResult[3:8])
275
+    return shift
276
+## end def
277
+
278
+def getIFwidth():
279
+    """
280
+    Description:  Gets the index of the width setting.  IF width settings
281
+                  vary according to the modulation type and bandwidth.
282
+                  Therefore only the index gets saved.
283
+    Parameters: none
284
+    Returns: string containing the amount index number.
285
+    """
286
+    if getRfAttn() == 'NA':
287
+        return 'NA'
288
+    sResult = sendCommand('SH0;')
289
+    width = int(sResult[3:5])
290
+    return width
291
+## end def
292
+
293
+def getContour():
294
+    """
295
+    Description:  Gets the four contour parameters.
296
+    Parameters: none
297
+    Returns: list object containing the four parameters.
298
+    """
299
+    if getRfAttn() == 'NA':
300
+        return [ 'NA', 'NA', 'NA', 'NA' ]
301
+    lContour = []
302
+
303
+    sResult = sendCommand('CO00;')
304
+    lContour.append(int(sResult[4:8]))
305
+
306
+    sResult = sendCommand('CO01;')
307
+    lContour.append(int(sResult[4:8]))
308
+
309
+    sResult = sendCommand('CO02;')
310
+    lContour.append(int(sResult[4:8]))
311
+
312
+    sResult = sendCommand('CO03;')
313
+    lContour.append(int(sResult[4:8]))
314
+    return lContour
315
+## end def
316
+
317
+def getDNRstate():
318
+    """
319
+    Description:  Gets digital noise reduction (DNR) state.
320
+    Parameters: none
321
+    Returns: 0 = OFF or 1 = ON
322
+    """
323
+    sResult = sendCommand('NR0;')
324
+    if sResult == '?;':
325
+        return 'NA'
326
+    state = sResult[3:4]
327
+    return bState.keys()[bState.values().index(state)]
328
+## end def
329
+
330
+def getDNRalgorithm():
331
+    """
332
+    Description:  Gets the algorithm used by the DNR processor.
333
+    Parameters: none
334
+    Returns: a number between 1 and 16 inclusive
335
+    """
336
+    sResult = sendCommand('RL0;')
337
+    algorithm = int(sResult[3:5])
338
+    return algorithm
339
+## end def
340
+
341
+def getDNFstate():
342
+    """
343
+    Description:  Gets digital notch filter (DNF) state.
344
+    Parameters: none
345
+    Returns: 0 = OFF or 1 = ON
346
+    """
347
+    sResult = sendCommand('BC0;')
348
+    if sResult == '?;':
349
+        return 'NA'
350
+    state = sResult[3:4]
351
+    return bState.keys()[bState.values().index(state)]
211 352
 ## end def
212 353
 
354
+def getNARstate():
355
+    """
356
+    Description:  Gets narrow/wide filter (NAR) state.
357
+    Parameters: none
358
+    Returns: 0 = Wide or 1 = Narrow
359
+    """     
360
+    sResult = sendCommand('NA0;')
361
+    if sResult == '?;':
362
+        return 'NA'
363
+    state = sResult[3:4]
364
+    return dNAR.keys()[dNAR.values().index(state)]
365
+## end def
366
+
367
+def getNotchState():
368
+    """
369
+    Description:  Gets the notch filter state and setting.
370
+    Parameters: none
371
+    Returns: a tuple containing state and frequency
372
+             state = 0 (OFF) or 1 (ON)
373
+             frequency = number between 1 and 320 (x 10 Hz)
374
+    """     
375
+    sResult = sendCommand('BP00;')
376
+    if sResult == '?;':
377
+        return ('NA', 'NA')
378
+    state = sResult[6:7]
379
+    state = bState.keys()[bState.values().index(state)]
380
+    sResult = sendCommand('BP01;')
381
+    freq = int(sResult[4:7])
382
+    return (state, freq)
383
+## end def
384
+
385
+
213 386
 #############################################################################
214
-# Define 'set' functions to encapsulate the various FT991 CAT commands.     #
387
+# Define 'set' methods to encapsulate the various FT991 CAT commands.       #
215 388
 #############################################################################
216 389
 
217 390
 def setMemory(dMem):
... ...
@@ -221,13 +394,13 @@ def setMemory(dMem):
221 394
                        defined:
222 395
 
223 396
                        memloc - the memory location to be written
224
-                       rxfreq - receive frequency of VFO-A in MHz
397
+                       vfoa - receive frequency of VFO-A in MHz
225 398
                        clarfreq - clarifier frequency and direction
226 399
                        rxclar - receive clarifier state
227 400
                        txclar - transmit clarifier state
228 401
                        mode - the modulation mode
229 402
                        encode - the tone or DCS encoding mode
230
-                       shift - the direction of the repeater shift
403
+                       rpoffset - the direction of the repeater shift
231 404
                        tag - a label for the memory location
232 405
 
233 406
     Returns: nothing
... ...
@@ -243,7 +416,7 @@ def setMemory(dMem):
243 416
     sCmd += '%0.3d' % iLocation
244 417
 
245 418
     # Validate and append the vfo-a frequency data.
246
-    iRxfreq = int(float(dMem['rxfreq']) * 1E6) # vfo-a frequency in Hz
419
+    iRxfreq = int(float(dMem['vfoa']) * 1E6) # vfo-a frequency in Hz
247 420
     if iRxfreq < 0.030E6 or iRxfreq > 450.0E6:
248 421
         raise Exception('VFO-A frequency must be between 30 kHz and ' \
249 422
                         '450 MHz, inclusive.')
... ...
@@ -259,13 +432,13 @@ def setMemory(dMem):
259 432
     # The following commands will automatically raise an exception if
260 433
     # incorrect data is supplied.  The exception will be a dictionary
261 434
     # object "key not found" error.
262
-    sCmd += dRxClar[dMem['rxclar']]
263
-    sCmd += dTxClar[dMem['txclar']]
435
+    sCmd += bState[dMem['rxclar']]
436
+    sCmd += bState[dMem['txclar']]
264 437
     sCmd += dMode[dMem['mode']]
265 438
     sCmd += '0'
266 439
     sCmd += dEncode[dMem['encode']]
267 440
     sCmd += '00'
268
-    sCmd += dShift[dMem['shift']]
441
+    sCmd += dShift[dMem['rpoffset']]
269 442
     sCmd += '0'
270 443
     sTag = dMem['tag']
271 444
 
... ...
@@ -342,7 +515,7 @@ def setRxClarifier(state='OFF'):
342 515
     """
343 516
     # An exception will automatically be raised if incorrect data is
344 517
     # supplied - most likely a "key not found" error.
345
-    sCmd = 'RT%s;' % dRxClar[state]
518
+    sCmd = 'RT%s;' % bState[state]
346 519
     # Send the completed command.
347 520
     sResult = sendCommand(sCmd)
348 521
     if sResult == '?;':
... ...
@@ -358,7 +531,7 @@ def setTxClarifier(state='OFF'):
358 531
     """
359 532
     # An exception will automatically be raised if incorrect data is
360 533
     # supplied - most likely a "key not found" error.
361
-    sCmd = 'XT%s;' % dTxClar[state]
534
+    sCmd = 'XT%s;' % bState[state]
362 535
     # Send the completed command.
363 536
     sResult = sendCommand(sCmd)
364 537
     if sResult == '?;':
... ...
@@ -386,6 +559,182 @@ def setMemoryLocation(iLocation):
386 559
         return str(iLocation)
387 560
 ## end def
388 561
 
562
+def setPreamp(state='IPO'):
563
+    """
564
+    Description:  Sends a formatted PA command that sets the preamplifier
565
+                  state.
566
+    Parameters: state - string 'IPO', 'AMP 1', 'AMP 2'
567
+    Returns: nothing
568
+    """
569
+    # An exception will automatically be raised if incorrect data is
570
+    # supplied - most likely a "key not found" error.
571
+    sCmd = 'PA0%s;' % dPreamp[state]
572
+    # Send the completed command.
573
+    sResult = sendCommand(sCmd)
574
+    if sResult == '?;':
575
+        raise Exception('setPreAmp error')
576
+## end def
577
+
578
+def setRfAttn(state='NA'):
579
+    """
580
+    Description:  Sends a formatted PA command that sets the RF attenuator
581
+                  state.  Note that attempting to write or read the
582
+                  attenuator for a VHF or UHF band results in an error.
583
+                  Hence the addition of a state 'NA' for NOT APPLICABLE.
584
+    Parameters: state - string 'OFF', 'ON', 'NA'
585
+    Returns: nothing
586
+    """
587
+    if state == 'NA':
588
+        return
589
+    sCmd = 'RA0%s;' % bState[state]
590
+    sResult = sendCommand(sCmd)
591
+    if sResult == '?;':
592
+        raise Exception('setRfAttn error')
593
+## end def
594
+
595
+def setNoiseBlanker(state='OFF'):
596
+    """
597
+    Description:  Sends a formatted NB command that sets the noise blanker
598
+                  state.
599
+    Parameters: state - string 'OFF', 'ON'
600
+    Returns: nothing
601
+    """
602
+    sCmd = 'NB0%s;' % bState[state]
603
+    sResult = sendCommand(sCmd)
604
+    if sResult == '?;':
605
+        raise Exception('setNoiseBlanker error')
606
+## end def
607
+
608
+def setIFshift(shift='NA'):
609
+    """
610
+    Description:  Sends a formatted IS command that sets the amount of
611
+                  IF shift.
612
+    Parameters: state - string 'OFF', 'ON'
613
+    Returns: nothing
614
+    """
615
+    if shift == 'NA':
616
+        return
617
+    shift = int(shift)
618
+    if abs(shift) > 1200:
619
+        raise Exception('setIFshift error: data out of bounds')
620
+    sCmd = 'IS0%0+5d;' % shift
621
+    sResult = sendCommand(sCmd)
622
+    if sResult == '?;':
623
+        raise Exception('setIFshift error')
624
+## end def
625
+
626
+def setIFwidth(index='NA'):
627
+    """
628
+    Description:  Sends a formatted SH command that sets the IF width
629
+                  IF shift.
630
+    Parameters: index of shift - value between 0 and 21, inclusive
631
+    Returns: nothing
632
+    """
633
+    if index == 'NA':
634
+        return
635
+    index = int(index)
636
+    if index < 0 or index > 21:
637
+        raise Exception('setIFwidth error: data out of bounds')
638
+    sCmd = 'SH0%02d;' % index
639
+    sResult = sendCommand(sCmd)
640
+    if sResult == '?;':
641
+        raise Exception('setIFwidth error')
642
+## end def
643
+
644
+def setContour(lParams):
645
+    """
646
+    Description:  Sends a formatted CO command that sets contour parameters
647
+    Parameters: lParams - list object containing the parameters
648
+    Returns: nothing
649
+    """
650
+    if lParams[0] == 'NA':
651
+        return
652
+
653
+    for inx in range(4):
654
+        sCmd = 'CO0%d%04d' % (inx, int(lParams[inx]))
655
+        sResult = sendCommand(sCmd)
656
+        if sResult == '?;':
657
+            raise Exception('setContour error')
658
+## end def
659
+
660
+def setDNRstate(state = 'NA'):
661
+    """
662
+    Description:  Sets the state (on or off) of the digital noise
663
+                  reduction (DNR) processor.
664
+    Parameters:   State = 0 (OFF) or 1 (ON)
665
+    Returns: nothing
666
+    """
667
+    if state == 'NA':
668
+        return
669
+    sCmd = 'NR0%01d' % int(bState[state])
670
+    sResult = sendCommand(sCmd)
671
+    if sResult == '?;':
672
+        raise Exception('setDNR error')
673
+## end def
674
+
675
+def setDNRalgorithm(algorithm = 1):
676
+    """
677
+    Description:  Sets the algorithm used by the digital noise
678
+                  reduction (DNR) processor.
679
+    Parameters:   algorithm - a number between 1 and 16 inclusive
680
+    Returns: nothing
681
+    """
682
+    sCmd = 'RL0%02d' % int(algorithm)
683
+    sResult = sendCommand(sCmd)
684
+    if sResult == '?;':
685
+        raise Exception('setDNR error')
686
+## end def
687
+
688
+def setDNFstate(state = 'NA'):
689
+    """
690
+    Description:  Sets the state (on or off) of the digital notch
691
+                  filter (DNF) processor.
692
+    Parameters:   State = 0 (OFF) or 1 (ON)
693
+    Returns: nothing
694
+    """
695
+    if state == 'NA':
696
+        return
697
+    sCmd = 'BC0%01d' % int(bState[state])
698
+    sResult = sendCommand(sCmd)
699
+    if sResult == '?;':
700
+        raise Exception('setDNF error')
701
+## end def
702
+
703
+def setNARstate( state = 'NA'):
704
+    """
705
+    Description:  Gets narrow/wide filter (NAR) state.
706
+    Parameters: none
707
+    Returns: 0 = Wide or 1 = Narrow
708
+    """
709
+    if state == 'NA':
710
+        return
711
+    sCmd = 'NA0%s' % dNAR[state]
712
+    sResult = sendCommand(sCmd)
713
+    if sResult == '?;':
714
+        raise Exception('setNAR error')
715
+## end def
716
+
717
+def setNotchState( state = ('NA', 'NA') ):
718
+    """
719
+    Description:  Gets the notch filter state and setting.
720
+    Parameters: none
721
+    Returns: a tuple containing state and frequency
722
+             state = 0 (OFF) or 1 (ON)
723
+             frequency = number between 1 and 320 (x 10 Hz)
724
+    """
725
+    if state[0] == 'NA':
726
+        return
727
+    sCmd = 'BP0000%s' % bState[state[0]]
728
+    sResult = sendCommand(sCmd)
729
+    if sResult == '?;':
730
+        raise Exception('setNotch error')
731
+    sCmd = 'BP01%03d' % int(state[1])
732
+    sResult = sendCommand(sCmd)
733
+    if sResult == '?;':
734
+        raise Exception('setNotch error')
735
+## end def
736
+
737
+
389 738
 #############################################################################
390 739
 # Helper functions to assist in various tasks.                              #
391 740
 #############################################################################
... ...
@@ -404,18 +753,29 @@ def parseCsvData(sline):
404 753
         return None
405 754
     # Store the parsed items with the appropriate key in the dictionary object.
406 755
     dChan['memloc'] = lchan[0]
407
-    dChan['rxfreq'] = lchan[1]
408
-    dChan['txfreq'] = lchan[2]
409
-    dChan['offset'] = lchan[3]
410
-    dChan['shift'] = lchan[4]
411
-    dChan['mode'] = lchan[5]
412
-    dChan['tag'] = lchan[6]
413
-    dChan['encode'] = lchan[7]
414
-    dChan['tone'] = lchan[8]
415
-    dChan['dcs'] = lchan[9]
416
-    dChan['clarfreq'] = lchan[10]
417
-    dChan['rxclar'] = lchan[11]
418
-    dChan['txclar'] = lchan[12]
756
+    dChan['vfoa'] = lchan[1]
757
+    dChan['vfob'] = lchan[2]
758
+    dChan['rpoffset'] = lchan[3]
759
+    dChan['mode'] = lchan[4]
760
+    dChan['tag'] = lchan[5]
761
+    dChan['encode'] = lchan[6]
762
+    dChan['tone'] = lchan[7]
763
+    dChan['dcs'] = lchan[8]
764
+    dChan['clarfreq'] = lchan[9]
765
+    dChan['rxclar'] = lchan[10]
766
+    dChan['txclar'] = lchan[11]
767
+    dChan['preamp'] = lchan[12]
768
+    dChan['rfattn'] = lchan[13]
769
+    dChan['nblkr']  = lchan[14]
770
+    dChan['shift'] = lchan[15]
771
+    dChan['width'] = lchan[16]
772
+    dChan['contour'] = [ lchan[17], lchan[18], lchan[19], lchan[20] ]
773
+    dChan['dnrstate'] = lchan[21]
774
+    dChan['dnralgorithm'] = lchan[22]
775
+    dChan['dnfstate'] = lchan[23]
776
+    dChan['narstate'] = lchan[24]
777
+    dChan['notchstate'] = lchan[25]
778
+    dChan['notchfreq'] = lchan[26]
419 779
     return dChan # return the dictionary object
420 780
 ## end def
421 781
 
... ...
@@ -561,7 +921,7 @@ def main():
561 921
     # Instantiate serial connection to FT991
562 922
     begin()
563 923
     # Set and receive a memory channel
564
-    dMem = {'memloc': '98', 'rxfreq': '146.52', 'shift': 'OFF', \
924
+    dMem = {'memloc': '98', 'vfoa': '146.52', 'shift': 'OFF', \
565 925
             'mode': 'FM', 'encode': 'TONE ENC', 'tag': 'KA7JLO', \
566 926
             'clarfreq': '1234', 'rxclar': 'ON', 'txclar': 'ON' \
567 927
            }
... ...
@@ -575,7 +935,7 @@ def main():
575 935
     getMemory(int(dMem['memloc']))
576 936
     print
577 937
     # Set and receive a memory channel
578
-    dMem = {'memloc': '99', 'rxfreq': '146.52', 'shift': 'OFF', \
938
+    dMem = {'memloc': '99', 'vfoa': '146.52', 'shift': 'OFF', \
579 939
             'mode': 'FM', 'encode': 'OFF', 'tag': 'KA7JLO', \
580 940
             'clarfreq': '0', 'rxclar': 'OFF', 'txclar': 'OFF' \
581 941
            }
Browse code

fixed but in read memory settings

Gandolf authored on 11/30/2019 04:34:27
Showing 1 changed files
... ...
@@ -132,8 +132,6 @@ def getMemory(memloc):
132 132
     # Send the get memory settings string to the FT991.
133 133
     sCmd = 'MT%0.3d;' % (memloc)
134 134
     sResult = sendCommand(sCmd)
135
-    if sResult == '?;':
136
-        return None
137 135
 
138 136
     # Parse memory settings string returned by the FT991
139 137
     memloc = sResult[2:5]
... ...
@@ -212,9 +210,9 @@ def getTxClarifier():
212 210
     return dTxClar.keys()[dTxClar.values().index(state)]
213 211
 ## end def
214 212
 
215
-#########################################################################
216
-# Define 'set' functions to encapsulate the various FT991 CAT commands. #
217
-#########################################################################
213
+#############################################################################
214
+# Define 'set' functions to encapsulate the various FT991 CAT commands.     #
215
+#############################################################################
218 216
 
219 217
 def setMemory(dMem):
220 218
     """
... ...
@@ -372,7 +370,8 @@ def setMemoryLocation(iLocation):
372 370
     Description:  Sends a formatted MC command that sets the current
373 371
                   memory location.
374 372
     Parameters: location - integer specifying memory location
375
-    Returns: nothing
373
+    Returns: None if the memory location is blank, otherwise
374
+             returns a string containing the memory location.
376 375
     """
377 376
     # Validate memory location data and send the command.
378 377
     if iLocation < 1 or iLocation > 118:
... ...
@@ -382,12 +381,14 @@ def setMemoryLocation(iLocation):
382 381
     # Send the completed command.
383 382
     sResult = sendCommand(sCmd)
384 383
     if sResult == '?;':
385
-        raise Exception('setMemoryLocation error')
384
+        return None
385
+    else:
386
+        return str(iLocation)
386 387
 ## end def
387 388
 
388
-############################################################################
389
-# Helper functions to assist in various tasks.                             #
390
-############################################################################
389
+#############################################################################
390
+# Helper functions to assist in various tasks.                              #
391
+#############################################################################
391 392
 
392 393
 def parseCsvData(sline):
393 394
     """
... ...
@@ -447,7 +448,9 @@ def sendCommand(sCmd):
447 448
     return sResult
448 449
 ## end def
449 450
 
450
-# Low level serial communications functions.
451
+#############################################################################
452
+# Low level serial communications functions.                                #
453
+#############################################################################
451 454
 
452 455
 def begin(baud=9600):
453 456
     """
... ...
@@ -562,28 +565,28 @@ def main():
562 565
             'mode': 'FM', 'encode': 'TONE ENC', 'tag': 'KA7JLO', \
563 566
             'clarfreq': '1234', 'rxclar': 'ON', 'txclar': 'ON' \
564 567
            }
565
-    setMemory(dMem)
566 568
     setMemoryLocation(int(dMem['memloc']))
569
+    setMemory(dMem)
567 570
     setRxClarifier(dMem['rxclar'])
568 571
     setTxClarifier(dMem['txclar'])
569 572
     setCTCSS('127.3 Hz')
570 573
     setDCS('115')
571 574
     print
572
-    getMemory(98)
575
+    getMemory(int(dMem['memloc']))
573 576
     print
574 577
     # Set and receive a memory channel
575 578
     dMem = {'memloc': '99', 'rxfreq': '146.52', 'shift': 'OFF', \
576 579
             'mode': 'FM', 'encode': 'OFF', 'tag': 'KA7JLO', \
577 580
             'clarfreq': '0', 'rxclar': 'OFF', 'txclar': 'OFF' \
578 581
            }
579
-    setMemory(dMem)
580 582
     setMemoryLocation(int(dMem['memloc']))
583
+    setMemory(dMem)
581 584
     setRxClarifier(dMem['rxclar'])
582 585
     setTxClarifier(dMem['txclar'])
583 586
     setCTCSS('141.3 Hz')
584 587
     setDCS('445')
585 588
     print
586
-    getMemory(99)
589
+    getMemory(int(dMem['memloc']))
587 590
     print
588 591
 
589 592
     # Test set commands
Browse code

set methods now upon errors raise exceptions

Gandolf authored on 11/28/2019 21:39:18
Showing 1 changed files
... ...
@@ -117,7 +117,9 @@ dDcs = { '23':'000', '25':'001', '26':'002', '31':'003', '32':'004',
117 117
 dRxClar = { 'OFF':'0', 'ON':'1' }
118 118
 dTxClar = { 'OFF':'0', 'ON':'1' }
119 119
 
120
-# Define 'get' methods to encapsulate FT991 commands that return status info.
120
+#############################################################################
121
+# Define 'get' methods to encapsulate FT991 commands returning status info. #
122
+#############################################################################
121 123
 
122 124
 def getMemory(memloc):
123 125
     """
... ...
@@ -127,17 +129,11 @@ def getMemory(memloc):
127 129
     """
128 130
     dMem = {}
129 131
 
130
-    # Set memory location pointer in FT991.  This is done
131
-    # by sending the memory location select (MC) command.
132
-    sCmd = 'MC%0.3d;' % (memloc)
133
-    sResult = sendCommand(sCmd)
134
-    # Skip blank memory locations, which return '?;'.
135
-    if sResult == '?;':
136
-        return None
137
-
138 132
     # Send the get memory settings string to the FT991.
139 133
     sCmd = 'MT%0.3d;' % (memloc)
140 134
     sResult = sendCommand(sCmd)
135
+    if sResult == '?;':
136
+        return None
141 137
 
142 138
     # Parse memory settings string returned by the FT991
143 139
     memloc = sResult[2:5]
... ...
@@ -188,7 +184,37 @@ def getDCS():
188 184
     return dDcs.keys()[dDcs.values().index(dcs)]
189 185
 ## end def
190 186
 
191
-# Define 'set' functions to encapsulate the various FT991 CAT commands.
187
+def getRxClarifier():
188
+    """
189
+    Description:  Gets the state of the Rx clarifier.
190
+    Parameters: none
191
+    Returns: string containing the state of the clarifier
192
+    """
193
+    # An exception will automatically be raised if incorrect data is
194
+    # supplied - most likely a "key not found" error.
195
+    sCmd = 'RT;'
196
+    sResult = sendCommand(sCmd)
197
+    state = sResult[2]
198
+    return dRxClar.keys()[dRxClar.values().index(state)]
199
+## end def
200
+
201
+def getTxClarifier():
202
+    """
203
+    Description:  Gets the state of the Tx clarifier.
204
+    Parameters: none
205
+    Returns: string containing the state of the clarifier
206
+    """
207
+    # An exception will automatically be raised if incorrect data is
208
+    # supplied - most likely a "key not found" error.
209
+    sCmd = 'XT;'
210
+    sResult = sendCommand(sCmd)
211
+    state = sResult[2]
212
+    return dTxClar.keys()[dTxClar.values().index(state)]
213
+## end def
214
+
215
+#########################################################################
216
+# Define 'set' functions to encapsulate the various FT991 CAT commands. #
217
+#########################################################################
192 218
 
193 219
 def setMemory(dMem):
194 220
     """
... ...
@@ -206,25 +232,17 @@ def setMemory(dMem):
206 232
                        shift - the direction of the repeater shift
207 233
                        tag - a label for the memory location
208 234
 
209
-    Returns: a string containing the formatted command
235
+    Returns: nothing
210 236
     """
211
-    # Format the set memory location (MC) command.
212
-    iMemloc = int(dMem['memloc'])
213
-    # Validate memory location data and send the command.
214
-    if iMemloc < 1 or iMemloc > 118:
237
+    # Format the set memory with tag command (MT).
238
+    sCmd = 'MT'
239
+
240
+    # Validate and append memory location data.
241
+    iLocation = int(dMem['memloc'])
242
+    if iLocation < 1 or iLocation > 118:
215 243
         raise Exception('Memory location must be between 1 and ' \
216 244
                         '118, inclusive.')
217
-    sCmd = 'MC%0.3d;' % iMemloc
218
-    sResult = sendCommand(sCmd)
219
-
220
-    # While the 'MW' and 'MT' commands can be used to turn the Rx
221
-    # and Tx clarifiers on, the clarifier states can only be turned
222
-    # off by sending the 'RT0' and 'XT0' commands.  This situation is
223
-    # probably due to a bug in the CAT interface.
224
-    sResult = sendCommand('RC;RT0;XT0;')
225
-
226
-    # Format the set memory with tag command (MT).
227
-    sCmd = 'MT%0.3d' % iMemloc
245
+    sCmd += '%0.3d' % iLocation
228 246
 
229 247
     # Validate and append the vfo-a frequency data.
230 248
     iRxfreq = int(float(dMem['rxfreq']) * 1E6) # vfo-a frequency in Hz
... ...
@@ -261,7 +279,8 @@ def setMemory(dMem):
261 279
 
262 280
     # Send the completed command.
263 281
     sResult = sendCommand(sCmd)
264
-    return sResult
282
+    if sResult == '?;':
283
+        raise Exception('setMemory error')
265 284
 ## end def
266 285
 
267 286
 def setCTCSS(tone):
... ...
@@ -270,12 +289,15 @@ def setCTCSS(tone):
270 289
                   CTCSS tone.
271 290
     Parameters:   tone - a string containing the CTCSS tone in Hz, e.g.,
272 291
                          '100 Hz'
273
-    Returns: a string containing the formatted command
292
+    Returns: nothing
274 293
     """
275 294
     # An exception will automatically be raised if incorrect data is
276 295
     # supplied - most likely a "key not found" error.
277 296
     sCmd = 'CN00%s;' % dTones[tone]
278
-    return sendCommand(sCmd)
297
+    # Send the completed command.
298
+    sResult = sendCommand(sCmd)
299
+    if sResult == '?;':
300
+        raise Exception('setCTCSS error')
279 301
 ## end def
280 302
 
281 303
 def setDCS(code):
... ...
@@ -283,12 +305,15 @@ def setDCS(code):
283 305
     Description:  Sends a formatted CN command that sets the desired
284 306
                   DCS code.
285 307
     Parameters: code - a string containing the DCS code, e.g., '23'
286
-    Returns: a string containing the formatted command
308
+    Returns: nothing
287 309
     """
288 310
     # An exception will automatically be raised if incorrect data is
289 311
     # supplied - most likely a "key not found" error.
290 312
     sCmd = 'CN01%s;' % dDcs[code]
291
-    return sendCommand(sCmd)
313
+    # Send the completed command.
314
+    sResult = sendCommand(sCmd)
315
+    if sResult == '?;':
316
+        raise Exception('setDCS error')
292 317
 ## end def
293 318
 
294 319
 def setPower(power):
... ...
@@ -296,17 +321,73 @@ def setPower(power):
296 321
     Description:  Sends a PC command that sets the desired
297 322
                   RF transmit power level.
298 323
     Parameters:   power - Watts, an integer between 5 and 100
299
-    Returns: a string containing the formatted command
324
+    Returns: nothing
300 325
     """
301 326
     power = int(power)
302 327
     # Validate power data and format command.
303 328
     if power < 5 or power > 100:
304 329
         raise Exception('Power must be between 0 and 100 watts, inclusive.')
305 330
     sCmd += 'PC%03.d;' % power
306
-    return sendCommand(sCmd)
331
+    # Send the completed command.
332
+    sResult = sendCommand(sCmd)
333
+    if sResult == '?;':
334
+        raise Exception('setPower error')
335
+
307 336
 ## end def
308 337
 
309
-# Helper functions to assist in various tasks.
338
+def setRxClarifier(state='OFF'):
339
+    """
340
+    Description:  Sends a formatted RT command that turns the Rx clarifier
341
+                  on or off.
342
+    Parameters: state - string 'OFF' or 'ON'
343
+    Returns: nothing
344
+    """
345
+    # An exception will automatically be raised if incorrect data is
346
+    # supplied - most likely a "key not found" error.
347
+    sCmd = 'RT%s;' % dRxClar[state]
348
+    # Send the completed command.
349
+    sResult = sendCommand(sCmd)
350
+    if sResult == '?;':
351
+        raise Exception('setRxClarifier error')
352
+## end def
353
+
354
+def setTxClarifier(state='OFF'):
355
+    """
356
+    Description:  Sends a formatted XT command that turns the Rx clarifier
357
+                  on or off.
358
+    Parameters: state - string 'OFF' or 'ON'
359
+    Returns: nothing
360
+    """
361
+    # An exception will automatically be raised if incorrect data is
362
+    # supplied - most likely a "key not found" error.
363
+    sCmd = 'XT%s;' % dTxClar[state]
364
+    # Send the completed command.
365
+    sResult = sendCommand(sCmd)
366
+    if sResult == '?;':
367
+        raise Exception('setTxClarifier error')
368
+## end def
369
+
370
+def setMemoryLocation(iLocation):
371
+    """
372
+    Description:  Sends a formatted MC command that sets the current
373
+                  memory location.
374
+    Parameters: location - integer specifying memory location
375
+    Returns: nothing
376
+    """
377
+    # Validate memory location data and send the command.
378
+    if iLocation < 1 or iLocation > 118:
379
+        raise Exception('Memory location must be an integer between 1 and ' \
380
+                        '118, inclusive.')
381
+    sCmd = 'MC%0.3d;' % iLocation
382
+    # Send the completed command.
383
+    sResult = sendCommand(sCmd)
384
+    if sResult == '?;':
385
+        raise Exception('setMemoryLocation error')
386
+## end def
387
+
388
+############################################################################
389
+# Helper functions to assist in various tasks.                             #
390
+############################################################################
310 391
 
311 392
 def parseCsvData(sline):
312 393
     """
... ...
@@ -476,14 +557,41 @@ def main():
476 557
 
477 558
     # Instantiate serial connection to FT991
478 559
     begin()
479
-    # Commands that send a setting
480
-    dMem = {'rxfreq': '146.52', 'shift': 'OFF', 'encode': 'OFF', \
481
-        'txclar': 'OFF', 'tag': 'KA7JLO', 'mode': 'FM', 'rxclar': 'OFF', \
482
-        'memloc': '99', 'clarfreq': '0'}
560
+    # Set and receive a memory channel
561
+    dMem = {'memloc': '98', 'rxfreq': '146.52', 'shift': 'OFF', \
562
+            'mode': 'FM', 'encode': 'TONE ENC', 'tag': 'KA7JLO', \
563
+            'clarfreq': '1234', 'rxclar': 'ON', 'txclar': 'ON' \
564
+           }
483 565
     setMemory(dMem)
484
-    sendCommand('MC002;')
485
-    # Commands that return data
566
+    setMemoryLocation(int(dMem['memloc']))
567
+    setRxClarifier(dMem['rxclar'])
568
+    setTxClarifier(dMem['txclar'])
569
+    setCTCSS('127.3 Hz')
570
+    setDCS('115')
571
+    print
572
+    getMemory(98)
573
+    print
574
+    # Set and receive a memory channel
575
+    dMem = {'memloc': '99', 'rxfreq': '146.52', 'shift': 'OFF', \
576
+            'mode': 'FM', 'encode': 'OFF', 'tag': 'KA7JLO', \
577
+            'clarfreq': '0', 'rxclar': 'OFF', 'txclar': 'OFF' \
578
+           }
579
+    setMemory(dMem)
580
+    setMemoryLocation(int(dMem['memloc']))
581
+    setRxClarifier(dMem['rxclar'])
582
+    setTxClarifier(dMem['txclar'])
583
+    setCTCSS('141.3 Hz')
584
+    setDCS('445')
585
+    print
486 586
     getMemory(99)
587
+    print
588
+
589
+    # Test set commands
590
+    #setMemoryLocation(2)
591
+    # Test get commands
592
+    #   commands...
593
+    # Test CAT commands via direct pass-through
594
+    # Commands that return data
487 595
     sendCommand('IF;')
488 596
     # Invalid command handling
489 597
     sendCommand('ZZZ;')
Browse code

minor documentation errors corrected and data validation added

Gandolf authored on 11/25/2019 20:49:16
Showing 1 changed files
... ...
@@ -117,67 +117,26 @@ dDcs = { '23':'000', '25':'001', '26':'002', '31':'003', '32':'004',
117 117
 dRxClar = { 'OFF':'0', 'ON':'1' }
118 118
 dTxClar = { 'OFF':'0', 'ON':'1' }
119 119
 
120
-# Define 'set' functions to encapsulate the various FT991 CAT commands.
120
+# Define 'get' methods to encapsulate FT991 commands that return status info.
121 121
 
122
-def setMemory(dMem):
122
+def getMemory(memloc):
123 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: 
124
+    Description: Get memory settings of a specific memory location.
125
+    Parameters: memloc - an integer specifying memory location 
126
+    Returns: a dictionary object containing the memory ettings
168 127
     """
169 128
     dMem = {}
170 129
 
171 130
     # Set memory location pointer in FT991.  This is done
172 131
     # by sending the memory location select (MC) command.
173
-    sCmd = 'MC%0.3d;' % (memLoc)
132
+    sCmd = 'MC%0.3d;' % (memloc)
174 133
     sResult = sendCommand(sCmd)
175 134
     # Skip blank memory locations, which return '?;'.
176 135
     if sResult == '?;':
177 136
         return None
178 137
 
179 138
     # Send the get memory settings string to the FT991.
180
-    sCmd = 'MT%0.3d;' % (memLoc)
139
+    sCmd = 'MT%0.3d;' % (memloc)
181 140
     sResult = sendCommand(sCmd)
182 141
 
183 142
     # Parse memory settings string returned by the FT991
... ...
@@ -207,9 +166,9 @@ def getMemory(memLoc):
207 166
 
208 167
 def getCTCSS():
209 168
     """
210
-    Description: 
211
-    Parameters: 
212
-    Returns: 
169
+    Description: Get the CTCSS tone setting for the current memory location.
170
+    Parameters: none
171
+    Returns: string containing the CTCSS tone
213 172
     """
214 173
     # Get result CTCSS tone
215 174
     sResult = sendCommand('CN00;')
... ...
@@ -219,9 +178,9 @@ def getCTCSS():
219 178
 
220 179
 def getDCS():
221 180
     """
222
-    Description: 
223
-    Parameters: 
224
-    Returns: 
181
+    Description: Get the DCS code setting for the current memory location.
182
+    Parameters: none
183
+    Returns: string containing the DCS code
225 184
     """
226 185
     # Get result of CN01 command
227 186
     sResult = sendCommand('CN01;')
... ...
@@ -229,41 +188,126 @@ def getDCS():
229 188
     return dDcs.keys()[dDcs.values().index(dcs)]
230 189
 ## end def
231 190
 
191
+# Define 'set' functions to encapsulate the various FT991 CAT commands.
192
+
193
+def setMemory(dMem):
194
+    """
195
+    Description: Sends a formatted MT command.
196
+    Parameters: dMem - a dictionary objected with the following keys
197
+                       defined:
198
+
199
+                       memloc - the memory location to be written
200
+                       rxfreq - receive frequency of VFO-A in MHz
201
+                       clarfreq - clarifier frequency and direction
202
+                       rxclar - receive clarifier state
203
+                       txclar - transmit clarifier state
204
+                       mode - the modulation mode
205
+                       encode - the tone or DCS encoding mode
206
+                       shift - the direction of the repeater shift
207
+                       tag - a label for the memory location
208
+
209
+    Returns: a string containing the formatted command
210
+    """
211
+    # Format the set memory location (MC) command.
212
+    iMemloc = int(dMem['memloc'])
213
+    # Validate memory location data and send the command.
214
+    if iMemloc < 1 or iMemloc > 118:
215
+        raise Exception('Memory location must be between 1 and ' \
216
+                        '118, inclusive.')
217
+    sCmd = 'MC%0.3d;' % iMemloc
218
+    sResult = sendCommand(sCmd)
219
+
220
+    # While the 'MW' and 'MT' commands can be used to turn the Rx
221
+    # and Tx clarifiers on, the clarifier states can only be turned
222
+    # off by sending the 'RT0' and 'XT0' commands.  This situation is
223
+    # probably due to a bug in the CAT interface.
224
+    sResult = sendCommand('RC;RT0;XT0;')
225
+
226
+    # Format the set memory with tag command (MT).
227
+    sCmd = 'MT%0.3d' % iMemloc
228
+
229
+    # Validate and append the vfo-a frequency data.
230
+    iRxfreq = int(float(dMem['rxfreq']) * 1E6) # vfo-a frequency in Hz
231
+    if iRxfreq < 0.030E6 or iRxfreq > 450.0E6:
232
+        raise Exception('VFO-A frequency must be between 30 kHz and ' \
233
+                        '450 MHz, inclusive.')
234
+    sCmd += '%0.9d' % iRxfreq
235
+
236
+    # Validate and append the clarifier data.
237
+    iClarfreq = int(dMem['clarfreq'])
238
+    if abs(iClarfreq) > 9999:
239
+        raise Exception('Clarifer frequency must be between -9999 Hz ' \
240
+                        'and +9999 Hz, inclusive.')
241
+    sCmd += '%+0.4d' % iClarfreq
242
+
243
+    # The following commands will automatically raise an exception if
244
+    # incorrect data is supplied.  The exception will be a dictionary
245
+    # object "key not found" error.
246
+    sCmd += dRxClar[dMem['rxclar']]
247
+    sCmd += dTxClar[dMem['txclar']]
248
+    sCmd += dMode[dMem['mode']]
249
+    sCmd += '0'
250
+    sCmd += dEncode[dMem['encode']]
251
+    sCmd += '00'
252
+    sCmd += dShift[dMem['shift']]
253
+    sCmd += '0'
254
+    sTag = dMem['tag']
255
+
256
+    # Validate and append the memory tag data.
257
+    if len(sTag) > 12:
258
+        raise Exception('Memory tags must be twelve characters or less.')
259
+    sCmd += '%-12s' % sTag
260
+    sCmd += ';' # Terminate the completed command.
261
+
262
+    # Send the completed command.
263
+    sResult = sendCommand(sCmd)
264
+    return sResult
265
+## end def
266
+
232 267
 def setCTCSS(tone):
233 268
     """
234
-    Description:  returns a formatted CN command that sets the desired
269
+    Description:  Sends a formatted CN command that sets the desired
235 270
                   CTCSS tone.
236 271
     Parameters:   tone - a string containing the CTCSS tone in Hz, e.g.,
237 272
                          '100 Hz'
238 273
     Returns: a string containing the formatted command
239 274
     """
275
+    # An exception will automatically be raised if incorrect data is
276
+    # supplied - most likely a "key not found" error.
240 277
     sCmd = 'CN00%s;' % dTones[tone]
241 278
     return sendCommand(sCmd)
242 279
 ## end def
243 280
 
244 281
 def setDCS(code):
245 282
     """
246
-    Description:  returns a formatted CN command that sets the desired
283
+    Description:  Sends a formatted CN command that sets the desired
247 284
                   DCS code.
248
-    Parameters:   code - a string containing the DCS code, e.g., '23'
285
+    Parameters: code - a string containing the DCS code, e.g., '23'
249 286
     Returns: a string containing the formatted command
250 287
     """
288
+    # An exception will automatically be raised if incorrect data is
289
+    # supplied - most likely a "key not found" error.
251 290
     sCmd = 'CN01%s;' % dDcs[code]
252 291
     return sendCommand(sCmd)
253 292
 ## end def
254 293
 
255 294
 def setPower(power):
256 295
     """
257
-    Description:  returns a formatted PC command that sets the desired
296
+    Description:  Sends a PC command that sets the desired
258 297
                   RF transmit power level.
259 298
     Parameters:   power - Watts, an integer between 5 and 100
260 299
     Returns: a string containing the formatted command
261 300
     """
262
-    sCmd = 'PC'
263
-    sCmd += '%03.d;' % power 
301
+    power = int(power)
302
+    # Validate power data and format command.
303
+    if power < 5 or power > 100:
304
+        raise Exception('Power must be between 0 and 100 watts, inclusive.')
305
+    sCmd += 'PC%03.d;' % power
264 306
     return sendCommand(sCmd)
265 307
 ## end def
266 308
 
309
+# Helper functions to assist in various tasks.
310
+
267 311
 def parseCsvData(sline):
268 312
     """
269 313
     Description:  stores each item in the comma delimited line in a single
... ...
@@ -286,14 +330,43 @@ def parseCsvData(sline):
286 330
     dChan['tag'] = lchan[6]
287 331
     dChan['encode'] = lchan[7]
288 332
     dChan['tone'] = lchan[8]
289
-    dChan['dcs'] = str(int(lchan[9]))
333
+    dChan['dcs'] = lchan[9]
290 334
     dChan['clarfreq'] = lchan[10]
291 335
     dChan['rxclar'] = lchan[11]
292 336
     dChan['txclar'] = lchan[12]
293 337
     return dChan # return the dictionary object
294 338
 ## end def
295 339
 
296
-# Define serial communications functions.
340
+def sendCommand(sCmd):
341
+    """
342
+    Description: Sends a formatted FT911 command to the communication
343
+                 port connected to the FT991.  Prints to stdout the
344
+                 answer from the FT991 (if any).
345
+    Parameters: device - a pointer to the FT991 comm port
346
+                sCmd - a string containing the formatted command
347
+    Returns: nothing
348
+    """
349
+    # Debug mode in conjunction with verbose mode is for verifying
350
+    # correct formatting of commands before they are actually sent
351
+    # to the FT991.
352
+    if verbose:
353
+        print sCmd,
354
+    # In debug mode do not actually send commands to the FT991.
355
+    if debug:
356
+        return ''
357
+
358
+    # Send the formatted command to the FT991 and get an answer, if any.
359
+    # If the command does not generate an answer, no characters will be
360
+    # returned by the FT991, resulting in an empty string returned by
361
+    # the receiveSerial function.
362
+    sendSerial(sCmd)
363
+    sResult  = receiveSerial();
364
+    if verbose:
365
+        print sResult
366
+    return sResult
367
+## end def
368
+
369
+# Low level serial communications functions.
297 370
 
298 371
 def begin(baud=9600):
299 372
     """
... ...
@@ -316,6 +389,7 @@ def begin(baud=9600):
316 389
     # In debug mode do not actually send commands to the FT991.
317 390
     if debug:
318 391
         return
392
+
319 393
     # Create a FT991 object for serial communication
320 394
     try:
321 395
         ptrDevice = serial.Serial(port, baud,      
... ...
@@ -332,35 +406,6 @@ def begin(baud=9600):
332 406
     return ptrDevice
333 407
 ## end def
334 408
 
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 409
 def receiveSerial(termchar=';'):
365 410
     """
366 411
     Description: Reads output one character at a time from the device
... ...
@@ -413,6 +458,10 @@ def sendSerial(command):
413 458
     ptrDevice.flushOutput() # Flush serial buffer to prevent overflows
414 459
 ## end def
415 460
 
461
+# Main routine only gets called when this module is run as a program rather
462
+# than imported into another python module.  Code testing the functions in
463
+# this module should be placed here.
464
+
416 465
 def main():
417 466
     """
418 467
     Description: Place code for testing this module here.
... ...
@@ -425,17 +474,19 @@ def main():
425 474
     verbose = True
426 475
     debug = False
427 476
 
477
+    # Instantiate serial connection to FT991
428 478
     begin()
429
-    sendCommand('IF;')
430
-    sendCommand('MC001;')
431
-    sendCommand('ZZZ;')
432
-
479
+    # Commands that send a setting
433 480
     dMem = {'rxfreq': '146.52', 'shift': 'OFF', 'encode': 'OFF', \
434 481
         'txclar': 'OFF', 'tag': 'KA7JLO', 'mode': 'FM', 'rxclar': 'OFF', \
435 482
         'memloc': '99', 'clarfreq': '0'}
436 483
     setMemory(dMem)
437
-    print getMemory(99)
438
-   
484
+    sendCommand('MC002;')
485
+    # Commands that return data
486
+    getMemory(99)
487
+    sendCommand('IF;')
488
+    # Invalid command handling
489
+    sendCommand('ZZZ;')
439 490
 ## end def
440 491
 
441 492
 if __name__ == '__main__':
Browse code

reorganize repository

Gandolf authored on 11/25/2019 06:37:12
Showing 1 changed files
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()