From www.AA6E.net
Jump to: navigation, search
#!/usr/bin/python
# This file name: octl010.py [rename if necessary]
# Curses-based Orion Control
# (c) 2004 Martin S. Ewing AA6E
# Requires pyserial package (pyserial.sourceforge.net)
#
import string, serial, time, sys, curses
#
# This is the serial port for communication with the Orion
#DEVICE="/dev/ttyS0"
DEVICE="/dev/ham.orion"
#
IDENT="OCTL v. 0.10  www.aa6e.net/aa6e"
#
ATTN= { '0':' off', '1':' 6dB', '2':'12dB', '3':'18dB' }
AGC = { 'O':' off', 'F':'fast', 'M':' med', 'S':'slow', 'P':'prog' }
STEP= { '1':'   1', '10':'  10', '100':' 100', '1000':'  1k', \
        '5000':'  5k', '10000':' 10k', '100000':'100k' }
ONOFF= { '0':'off', '1':' on' }
MODE= { '0':'USB', '1':'LSB', '2':'UCW', '3':'LCW', '4':' AM', '5':' FM', \
        '6':'FSK' }
LL=  { 'S':'  - ', 'M':'main', 'B':'main', 'N':'  - ' }
RR=  { 'S':'sub', 'M':' - ', 'B':'sub', 'N':' - ' }
#
def rd():
  MAX=20; c=''; ans=''; i=0
  while c <> '\r':
    c = ser.read(1)
    # Timeout happens when Orion gets funky.  Next time should be ok.
    if c == '': break           #timeout? try again?
    ans += c
    i += 1
    if i > MAX : raise MyExcept, "serial read too long"
  return ans[:-1]       # trim off final \r
#
def wrt(s):
    if ser.inWaiting(): ser.flushInput() # Orion i/o has slipped
    ser.write(s+'\r')
# The common send/get combination
def oget(ss):   # n = size of return data expected
   MAX=5; t = ''; i=0;
   for i in range(MAX):
     wrt(ss)
     t = rd()
     if len(t)<len(ss) : continue       # read string too short
     if t[1:len(ss)] == ss[1:] : break  # resp. looks good
   return t[len(ss):]                   # return data part only
# Suppress leading zeroes of 3 char string
def slz(xx) :
   if len(xx) <>3 : return '?'
   m1=xx[0]; m2=xx[1]; m3=xx[2]
   if m1=='0':
     m1=' '
     if m2=='0': m2=' '
   return m1+m2+m3
# Test a string to see if it converts to an integer, return T/F
def validint(s):
   try:
     i=int(s)		# simple validity check
   except ValueError: return False
   return True
#
#
ser = serial.Serial(DEVICE,baudrate=57600,rtscts=1,timeout=0.2)
ser.flushInput()
ser.flushOutput()
mw = curses.initscr()
curses.noecho()
curses.cbreak()
mw.keypad(1)
curses.start_color()
wrt("XX")		# Orion ID = syncs the serial port
msg1=rd()
wrt("?V")		# Firmware version number
msg2=rd()
#
curses.init_pair(1, curses.COLOR_RED, curses.COLOR_WHITE)
curses.init_pair(2, curses.COLOR_BLACK, curses.COLOR_YELLOW)
curses.init_pair(3, curses.COLOR_BLACK, curses.COLOR_CYAN)
curses.init_pair(4, curses.COLOR_GREEN, curses.COLOR_BLACK)
# Set up main window static stuff
mw.addstr(0,0, \
"          |   TUNE    |   MIC     |  S-TONE   |   VOX    |   AUDIO   |")
mw.addstr(1,0, \
"          |           |           |           |          |           |")
mw.addstr(2,0, \
"   PWR                                                                  MENUS")
mw.addstr(4,0, \
"   MON                                                                  ATTN m")
mw.addstr(5,0, \
"                                                                        ATTN s")
mw.addstr(6,0, \
"ssb SP                                                                  AGC")
mw.addstr(7,0,  \
" cw SP")
mw.addstr(8,0, \
"SEND 1                                                                  SWEEP")
mw.addstr(10,0, \
"SEND 2                                                                  STEP m")
mw.addstr(11,0, \
"                                                                             s")
mw.addstr(12,0, \
"SEND 3                                                                  SPOT")
mw.addstr(14,0, \
"USER 1                                                                  RF G m")
mw.addstr(15,0, \
"                                                                             s")
mw.addstr(16,0, \
"USER 2                                                                  PREAMP")
mw.addstr(18,0, \
"                                                                        MODE")
mw.addstr(21,0, \
"          |           |           |           |          |           | ")
mw.addstr(22,0, \
"          |  RECALL   |    NB     |    NR     |   NOTCH  |    AM     | ")
#
mw.addstr(23,0, \
(" Type 'q' to quit.  "+IDENT+"     "+"Firmware "+msg2).center(79), \
  curses.color_pair(1))
# make a box
mw.hline(2,12,curses.ACS_HLINE,55,curses.color_pair(4))
mw.hline(20,12,curses.ACS_HLINE,55,curses.color_pair(4))
mw.vline(2,12,curses.ACS_VLINE,18,curses.color_pair(4))
mw.vline(2,66,curses.ACS_VLINE,18,curses.color_pair(4))
mw.addch(2,12,curses.ACS_ULCORNER,curses.color_pair(4))
mw.addch(2,66,curses.ACS_URCORNER,curses.color_pair(4))
mw.addch(20,12,curses.ACS_LLCORNER,curses.color_pair(4))
mw.addch(20,66,curses.ACS_LRCORNER,curses.color_pair(4))
mw.nodelay(1)
# Static part of VFO/Freq window
mw.addstr(03,18, \
"VFO A FREQUENCY                         ", curses.color_pair(3))
mw.addstr(04,18, \
"VFO B FREQUENCY                         ", curses.color_pair(3))
#
f1=0
f2=0
loopctr = 0
while (1):
   loopctr += 1
   if f1: mw.addstr(0,0,"+")
   else:  mw.addstr(0,0,"-")
   f1 = not f1
   vfoa=oget("?AF")	# Get VFO A Freq
   if len(vfoa)==8 :
     mhz = (vfoa[0:2].lstrip('0')).rjust(2)  # strip leading 0's
     mw.addstr(3,36, mhz+'.'+vfoa[2:5]+'.'+vfoa[5:], curses.color_pair(2))
   vfob=oget("?BF")   # Get VFO B Freq
   if len(vfob)==8 :
     mhz = (vfob[0:2].lstrip('0')).rjust(2)  # strip leading 0's
     mw.addstr(4,36, mhz+'.'+vfob[2:5]+'.'+vfob[5:], curses.color_pair(2))
   vasn=oget("?KV")	# Get VFO Assignments
   if len(vasn) == 3:	# Must be 3 chars.  If not, skip
     mw.addstr(3,49,'          ', curses.color_pair(3))
     mw.addstr(4,49,'          ', curses.color_pair(3))
     if   vasn[2] == 'A' : mw.addstr(3,49,'Tx', curses.color_pair(2))
     elif vasn[2] == 'B' : mw.addstr(4,49,'Tx', curses.color_pair(2))
     if   vasn[1] == 'A' : mw.addstr(3,55,'SbRx', curses.color_pair(2))
     elif vasn[1] == 'B' : mw.addstr(4,55,'SbRx', curses.color_pair(2))
     if   vasn[0] == 'A' : mw.addstr(3,52,'Rx', curses.color_pair(2))
     elif vasn[0] == 'B' : mw.addstr(4,52,'Rx', curses.color_pair(2))
# End of frequency window stuff
# Now, do the top row data of main window
#
   mgn=oget("?TM")		# Mic Gain
   mgnx = slz(mgn)
   mw.addstr(1,26,mgnx)
   stv = oget("?CV")		# Side Tone Level
   stvx= (3-len(stv)) * ' ' + stv
   mw.addstr(1,38,stvx)
   vox=oget("?TV")		# Vox on/off
   if vox=='1': mw.addstr(1,50,"on ")
   else :       mw.addstr(1,50,"off")
# Now, do left-hand values
   txp=oget("?TP")		# Tx Power
# Would like to show "off" if pwr=0, but we never get this.
   mw.addstr(2,8,txp)
   mon=oget("?TO")		# Mon level
   monx = slz(mon)  # suppress leading zeroes
   mw.addstr(4,8,monx)
# SP is multiplexed depending on mode
# SSB Mode = Speech Processor level
# values = 0 - 9 (single digit, 0 = "off")
   sp=oget("?TS")		# SP level
   if len(sp) == 1:
     if sp=='0': spx='off'
     else : spx='  '+sp
     mw.addstr(6,8,spx)
# CW Mode = Keyer speed (0=off)
   kon=oget("?CK")		# Keyer on/off
   if kon=='0': keysp='off'
   else :
     ksp0=oget("?CS")		# Keyer speed
     keysp = ' '+ksp0		# always 2 digits >= 10
   mw.addstr(7,8,keysp)
# Bottom row of values
# Except none of them are available?
# Right hand column
   try:
     matt=oget("?RMT")		# Main Attn
     mw.addstr(4,67,ATTN[matt])
     satt=oget("?RST")		# Sub Attn
     mw.addstr(5,67,ATTN[satt])
     magc=oget("?RMA")		# Main AGC
     mw.addstr(6,67,AGC[magc])
     sagc=oget("?RSA")		# Sub AGC  BUT GIVES MAIN AGC??
     mw.addstr(7,67,AGC[sagc])
     mstep=oget("?RMI")		# Main Step
     mw.addstr(10,67,STEP[mstep])
     sstep=oget("?RSI")		# Sub Step  BUT GIVES MAIN STEP??
     mw.addstr(11,67,STEP[mstep]) 
   except KeyError: 		# restart if fmt error
     mw.addstr(0,1,"W")
     mw.refresh()
     time.sleep(0.5)
     wrt("XX")
     msg=rd()
     time.sleep(0.5)
     mw.addstr(0,1," ")
     mw.refresh()
   mrfg=oget("?RMG")		# Main RF Gain
   mw.addstr(14,68,mrfg.rjust(3))
   srfg=oget("?RSG")		# Sub RF Gain
   mw.addstr(15,68,srfg.rjust(3))
   mpre=oget("?RME")		# (main) preamp
   try:
     mw.addstr(16,68,ONOFF[mpre])
   except KeyError: pass
# S Meter / VSWR line
   signal=oget("?S")		# multiplex, depending on R/T mode
   if signal[1] == 'M':		# We are receiving?
     if len(signal) == 9:	# check for normal length
				# scale 0 to 20 units
       msrdg = int(signal[3:5])/4  # 80 is max, more or less
       ssrdg = int(signal[7:9])/2  # 40 max on subrx
       mw.addstr(6,14,"|....................|  S")
       mw.addstr(6,41,"|....................|")
       msrdg = max(1,min(20,msrdg)) # Insure sanity
       ssrdg = max(1,min(20,ssrdg))
       mw.addstr(6,15,msrdg*'=', curses.color_pair(2))
       mw.addstr(6,42,ssrdg*'=', curses.color_pair(2))
   if signal[1] == 'F': 	# We are transmitting?
       mw.addstr(6,14,"|....(transmit)......|")
       s = signal[2:]
       if len(s) < 20 : s = s + (20-len(s))*' '
       mw.addstr(6,41,'  '+s)
# OK, now do the volume indicators (are these better than numerics?)
# ???????? How to tell if audio is 'muted'?
   mvol=oget("?UM")		# Mail volume
   if len(mvol) == 3:		# s.b. 000-255
      try:			# worry if numeric
        mvolv = int(mvol)
      except ValueError: mvolv = 0 # take a safe number, if trouble
      mw.addstr(7,14,"<....................> Vol")
      mvolv = int( float(mvolv) * 20.0/255.0)
      mw.addstr(7,15,mvolv*'o')
   svol=oget("?US")
   if len(svol) == 3:
      try:
        svolv = int(svol)
      except ValueError: svolv = 0
      mw.addstr(7,41,"<....................>")
      svolv = int( float(svolv) * 20.0/255.0)
      mw.addstr(7,42,svolv*'o')
# Receiver modes
   mmode=oget("?RMM")		# 0-6 integer
   try:
     mw.addstr(8,14,"MAIN     "+MODE[mmode])
   except KeyError: pass
   smode=oget("?RSM")
   try:
     mw.addstr(8,41,"SUB      "+MODE[smode])
   except KeyError: pass
# Bandwidths
   mbw=oget("?RMF")		# 100 - 6000 Hz BW
   if len(mbw)==3 : mbw = ' ' + mbw
   if len(mbw)==4:
      mw.addstr(9,14,"BW:     "+mbw)
   sbw=oget("?RSF")
   if len(sbw)==3 : sbw = ' ' + sbw
   if len(sbw)==4:
      mw.addstr(9,41,"BW:     "+sbw)
# PBT settings
   mpbt=oget("?RMP")		# returns signed var len str (1-5 chars)
   if mpbt[0] <> '-' : mpbt = '+' + mpbt # want explicit '+' sign
   if not validint(mpbt) : mpbt = ' '
   mpbt = mpbt.rjust(5)		# Guarantee width 5
   mw.addstr(10,14,"PBT:   "+mpbt)
   spbt=oget("?RSP")		# returns signed var len str (1-5 chars)
   if spbt[0] <> '-' : spbt = '+' + spbt # want explicit '+' sign
   if not validint(spbt) : spbt = ' '
   spbt = spbt.rjust(5)		# Guarantee width 5
   mw.addstr(10,41,"PBT:   "+spbt)
# RIT settings
# ??? How to test if RIT is active?  
   mrit=oget("?RMR")
   if mrit[0] <> '-' : mrit = '+' + mrit
   if not validint(mrit) : mrit = ' '
   mrit = mrit.rjust(5)
   mw.addstr(11,14,"RIT:   "+mrit)
   srit=oget("?RSR")
   if srit[0] <> '-' : srit = '+' + srit
   srit = srit.rjust(5)
   mw.addstr(11,41,"RIT:   "+srit)
# XIT for main
# ??? How to know if active?
   xit=oget("?RMX")
   if xit[0] <> '-' : xit = '+' + xit
   if not validint(xit) : xit = ' '
   xit = xit.rjust(5)
   mw.addstr(12,14,"XIT:   "+xit)
# Antenna selections
   ants=oget("?KA")		# 3 chars [ S M B N ]
   try:
     mw.addstr(16,14,LL[ants[0]]+" Ant 1 "+RR[ants[0]])
     mw.addstr(17,14,LL[ants[1]]+" Ant 2 "+RR[ants[1]])
     mw.addstr(18,14,LL[ants[2]]+" RxAnt "+RR[ants[2]])
   except KeyError, IndexError: pass
# ?? Can't get main/sub select buttons?
# ?? Can't get RIT/XIT select buttons?
# (The VFOx > M buttons are only momentary.)
# Also can't get lock/unlock, although ?AL does something...
   mw.move(23,0)
   mw.refresh()
   time.sleep(0.2)		# Slow it all down to keep Orion happy?
   c = mw.getch()
   if (c == -1): continue
   if chr(c) == 'q': break
curses.nocbreak()
mw.keypad(0)
curses.echo()
curses.endwin()
ser.close()
print
print
print "That's all folks!"