# This file supports various hardware boxes at my shack from __future__ import print_function from __future__ import absolute_import from __future__ import division import sys, struct, math, socket, select, time, traceback, os import wx, wx.lib.buttons DEBUG = 0 gatewayTime = 0 # time of last gateway command gatewayLimit = 0.2 # minimum time between gateway commands class ControlBox: # Control my station control box address = ('192.168.1.194', 0x3A00 + 64) def __init__(self, app, conf): self.application = app # Application instance (to provide attributes) self.conf = conf # Config file module self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.socket.setblocking(0) self.socket.connect(self.address) self.have_data = b'C\000' self.want_data = b'C\000' self.timer = 0 def close(self): self.want_data = b'C\000' # raise key if down if self.have_data != self.want_data: self.socket.send(self.want_data) time.sleep(0.1) self.socket.send(self.want_data) def OnButtonPTT(self, event): btn = event.GetEventObject() if btn.GetValue(): # Turn the software key bit on or off self.want_data = b'C\001' else: self.want_data = b'C\000' def SetKeyDown(self, down): if down: # Turn the software key bit on or off self.want_data = b'C\001' else: self.want_data = b'C\000' def HeartBeat(self): global gatewayTime if not self.socket: return try: # The control box echoes its commands self.have_data = self.socket.recv(50) except socket.error: pass except socket.timeout: pass if self.have_data != self.want_data and time.time() - gatewayTime > gatewayLimit: gatewayTime = time.time() if self.timer <= 10: self.timer += 1 if self.timer == 10: print ('Control box error') try: self.socket.send(self.want_data) except socket.error: pass except socket.timeout: pass class LowPassFilter: # Control my low pass filter box address = ('192.168.1.194', 0x3A00 + 39) # Filters are numbered 1 thru 8 for bands: 80, 15, 60, 40, 30, 20, 17, short lpfnum = (1, 1, 1, 1, 1, 3, # frequency 0 thru 5 MHz 4, 4, 5, 5, 5, # 6 thru 10 6, 6, 6, 6, 7, # 11 thru 15 7, 7, 7, 2, 2, # 16 thru 20 2, 2, 8, 8, 8) # 21 thru 25; otherwise the filter is 8 def __init__(self, app, conf): self.application = app # Application instance (to provide attributes) self.conf = conf # Config file module self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.socket.setblocking(0) self.socket.connect(self.address) self.have_data = None self.want_data = b'\000' self.old_tx_freq = 0 self.timer = 0 def ChangeBand(self, band): pass def SetTxFreq(self, tx_freq): if not self.socket: return # Filters are numbered 1 thru 8 if abs(self.old_tx_freq - tx_freq) < 100000: return # Ignore small tuning changes self.old_tx_freq = tx_freq try: # Look up filter number based on MHz num = self.lpfnum[tx_freq // 1000000] except IndexError: num = 8 self.want_data = bytearray((num, )) self.timer = 0 #print ("LP filter band %d" % num) def HeartBeat(self): global gatewayTime if not self.socket: return try: # The HP filter box echoes its commands self.have_data = self.socket.recv(50) except socket.error: pass except socket.timeout: pass if self.have_data != self.want_data and time.time() - gatewayTime > gatewayLimit: gatewayTime = time.time() if self.timer <= 10: self.timer += 1 if self.timer == 10: print ('Low pass filter error') try: self.socket.send(self.want_data) except socket.error: pass except socket.timeout: pass class HighPassFilter: # Control my high pass filter box address = ('192.168.1.194', 0x3A00 + 21) def __init__(self, app, conf): self.application = app # Application instance (to provide attributes) self.conf = conf # Config file module self.preamp = 0 self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.socket.setblocking(0) self.socket.connect(self.address) self.have_data = None self.want_data = bytes(3) self.old_tx_freq = 0 self.timer = 0 def ChangeBand(self, band): btn = self.application.BtnRfGain freq = self.application.VFO + self.application.txFreq if self.conf.use_sdriq: if btn: if freq < 5000000: btn.SetLabel('RF -10', True) elif freq < 13000000: btn.SetLabel('RF 0 dB', True) else: btn.SetLabel('RF +16', True) elif self.conf.use_rx_udp: if btn: if freq < 5000000: btn.SetLabel('RF 0 dB', True) elif freq < 13000000: btn.SetLabel('RF 0 dB', True) else: btn.SetLabel('RF +16', True) def OnButtonRfGain(self, event): """Set my High Pass Filter Box preamp gain and attenuator state.""" btn = event.GetEventObject() n = btn.index if n == 0: # 0dB self.preamp = 0x00 self.application.Hardware.rf_gain = 0 elif n == 1: # +16 self.preamp = 0x02 self.application.Hardware.rf_gain = 16 elif n == 2: # -20 self.preamp = 0x0C self.application.Hardware.rf_gain = -20 elif n == 3: # -10 self.preamp = 0x04 self.application.Hardware.rf_gain = -10 else: print ('Unknown RfGain') self.application.Hardware.rf_gain = 0 self.SetTxFreq(None) def SetTxFreq(self, tx_freq): """Set high pass filter and preamp/attenuator state""" # Filter cutoff in MHz: 0.0, 2.7, 3.95, 5.7, 12.6, 18.2, 22.4 # Frequency MHz Bits Hex Band # ============= ==== === ==== # 0 to 2.70 PORTD, 0 0x01 160 # 2.7 to 3.95 PORTB, 1 0x02 80 # 3.95 to 5.70 PORTD, 7 0x80 60 # 5.70 to 12.60 PORTB, 0 0x01 40, 30 # 12.60 to 18.20 PORTD, 6 0x40 20, 17 # 18.20 to 22.40 PORTB, 7 0x80 15 # 22.40 to 99.99 PORTB, 6 0x40 12, 10 # Other bits: Preamp PORTD 0x02, Atten1 PORTD 0x04, Atten2 PORTD 0x08 if not self.socket: return if tx_freq is None: tx_freq = self.old_tx_freq elif abs(self.old_tx_freq - tx_freq) < 100000: return # Ignore small tuning changes self.old_tx_freq = tx_freq portb = portc = portd = 0 if self.conf.use_sdriq: if tx_freq < 15000000: # Turn preamp on/off self.preamp = 0x00 else: self.preamp = 0x02 elif self.conf.use_rx_udp: pass # self.preamp is already set else: # turn preamp off self.preamp = 0x00 if tx_freq < 12600000: if tx_freq < 3950000: if tx_freq < 2700000: portd = 0x01 else: portb = 0x02 elif tx_freq < 5700000: portd = 0x80 else: portb = 0x01 elif tx_freq < 18200000: portd = 0x40 elif tx_freq < 22400000: portb = 0x80 else: portb = 0x40 portd |= self.preamp self.want_data = bytearray((portb, portc, portd)) self.timer = 0 def HeartBeat(self): global gatewayTime if not self.socket: return try: # The HP filter box echoes its commands self.have_data = self.socket.recv(50) except socket.error: pass except socket.timeout: pass if self.have_data != self.want_data and time.time() - gatewayTime > gatewayLimit: gatewayTime = time.time() if self.timer <= 10: self.timer += 1 if self.timer == 10: print ('High pass filter error') try: self.socket.send(self.want_data) except socket.error: pass except socket.timeout: pass class FilterBoxV2: # Control my 2016 high/low pass filter box address = ('192.168.1.194', 0x3A00 + 70) # Low pass filters are numbered 0 thru 5 for bands: 10, 15, 17, 20, 40, 60 lpfnum = (5, 5, 5, 5, 5, 5, # frequency 0 thru 5 MHz 4, 4, 3, 3, 3, # 6 thru 10 3, 3, 3, 3, 2, # 11 thru 15 2, 2, 2, 1, 1, # 16 thru 20 1, 0, 0, 0, 0) # 21 thru 25; otherwise the filter is 1 # High pass filters are numbered 0 thru 2 with cutoff 12.5 MHz, 4.2 MHz, short hpfnum = (2, 2, 2, 2, 2, 1, # frequency 0 thru 5 MHz 1, 1, 1, 1, 1, # 6 thru 10 1, 1, 0, 0, 0, # 11 thru 15 0, 0, 0, 0, 0, # 16 thru 20 0, 0, 0, 0, 0) # 21 thru 25; otherwise the filter is 1 def __init__(self, app, conf): self.application = app # Application instance (to provide attributes) self.conf = conf # Config file module self.preamp = 0 self.have_data = None self.want_data = b'\x00\x00' self.old_tx_freq = 0 self.timer = 0 self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.socket.setblocking(0) self.socket.connect(self.address) def ChangeBand(self, band): pass def DefaultPreamp(self, freq): preamp = 0 if self.conf.use_rx_udp: btn = self.application.BtnRfGain if btn: if freq < 5000000: btn.SetLabel('RF 0 dB', False) elif freq < 13000000: btn.SetLabel('RF 0 dB', False) else: btn.SetLabel('RF +16', False) preamp = 1 return preamp def OnButtonRfGain(self, event): btn = event.GetEventObject() n = btn.index if n == 0: # 0dB self.preamp = 0 self.application.Hardware.rf_gain = 0 elif n == 1: # +16 self.preamp = 1 self.application.Hardware.rf_gain = 16 else: print ('Unknown RfGain') self.preamp = 0 self.application.Hardware.rf_gain = 0 #print ('Gain', self.preamp, self.application.Hardware.rf_gain) self.SetTxFreq(None) def SetPreamp(self, preamp): if preamp: self.preamp = 1 else: self.preamp = 0 self.SetTxFreq(None) def SetTxFreq(self, tx_freq): if tx_freq is None: tx_freq = self.old_tx_freq elif abs(self.old_tx_freq - tx_freq) < 100000: return # Ignore small tuning changes if abs(self.old_tx_freq - tx_freq) > 1000000: self.preamp = self.DefaultPreamp(tx_freq) self.old_tx_freq = tx_freq try: # Look up low pass filter number based on MHz lpf = self.lpfnum[tx_freq // 1000000] except IndexError: lpf = 0 try: # Look up high pass filter number based on MHz hpf = self.hpfnum[tx_freq // 1000000] except IndexError: hpf = 0 #print ("V2 filter LPF %d HPF %d" % (lpf, hpf)) lpf = 1 << lpf hpf = 1 << hpf if self.preamp: hpf |= 0b10000000 self.want_data = bytearray((lpf, hpf)) self.timer = 0 def HeartBeat(self): global gatewayTime if not self.socket: return try: # The V2 filter box echoes its commands self.have_data = self.socket.recv(50) except socket.error: pass except socket.timeout: pass if self.have_data != self.want_data and time.time() - gatewayTime > gatewayLimit: gatewayTime = time.time() if self.timer <= 10: self.timer += 1 if self.timer == 10: print ('V2 filter box error') try: self.socket.send(self.want_data) #print ('V2 filter box send data 0x%X 0x%X' % (ord(self.want_data[0]), ord(self.want_data[1]))) except socket.error: pass except socket.timeout: pass class AntennaControl: # Control my KI8BV dipole AntCtrlAddress = ('192.168.1.194', 0x3A00 + 33) def __init__(self, app, conf): self.application = app # Application instance (to provide attributes) self.conf = conf # Config file module self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.socket.setblocking(0) self.socket.connect(self.AntCtrlAddress) self.have_data = None self.want_data = b'\00' self.timer = 0 def SetTxFreq(self, tx_freq): self.timer = 0 if tx_freq < 19000000: self.want_data = b'\03' elif tx_freq < 22000000: self.want_data = b'\02' elif tx_freq < 26500000: self.want_data = b'\01' else: self.want_data = b'\00' def HeartBeat(self): global gatewayTime try: # The antenna control box echoes its commands self.have_data = self.socket.recv(50) except socket.error: pass except socket.timeout: pass if self.have_data != self.want_data and time.time() - gatewayTime > gatewayLimit: gatewayTime = time.time() self.timer += 1 if self.timer == 10: print ('Antenna control error') self.timer = 0 try: self.socket.send(self.want_data) #print ("Change dipole to ord %d" % ord(self.want_data)) except socket.error: pass except socket.timeout: pass class AntennaTuner: # Control my homebrew antenna tuner and my KI8BV dipole address = ('192.168.1.194', 0x3A00 + 47) def __init__(self, app, conf): self.application = app # Application instance (to provide attributes) self.conf = conf # Config file module self.socket = None self.have_data = b'\xFF\xFF\xFF' self.tx_freq = 0 self.tune_freq = 0 # Frequency we last tuned self.timer = 0 self.set_L = 0 self.set_C = 0 self.set_HighZ = 0 self.antnum = 0 # Antenna number 0 or 1 self.dipole2 = AntennaControl(app, conf) # Control the KI8BV dipole if conf.use_rx_udp == 10: # Hermes UDP protocol for Hermes-Lite2 path = 'TunerLCZ_HL2.txt' else: path = 'TunerLCZ.txt' if sys.platform == "win32": path = 'C:/pub/' + path else: path = '/home/jim/pub/' + path fp = open(path, "r") lines = fp.readlines() fp.close() self.TunerLCZ = [(0, 0, 0, 0), (999111000, 0, 0, 0)] # Add dummy first and last entry. for line in lines: freq, antL, antC, hilo = line.split() freq = int(freq) antL = int(antL) antC = int(antC) hilo = int(hilo) self.TunerLCZ.append((freq, antL, antC, hilo)) self.TunerLCZ.sort() self.WantData() def WantData(self): if self.set_HighZ: flags = 0x00 else: flags = 0x08 if self.antnum: flags |= 0x04 self.want_data = bytearray((self.set_C, self.set_L, flags)) def open(self): self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.socket.setblocking(0) self.socket.connect(self.address) def close(self): pass def OnSpot(self, level): # level is -1 for Spot button Off; else the Spot level 0 to 1000. pass def ChangeBand(self, band): pass def FindLCZ(self, tx_freq): i1 = 0 i2 = len(self.TunerLCZ) - 1 while 1: # binary partition i = (i1 + i2) // 2 if self.TunerLCZ[i][0] < tx_freq: i1 = i else: i2 = i if i2 - i1 <= 1: break # The correct setting is between i1 and i2. if tx_freq == self.TunerLCZ[i2][0]: index = i2 else: index = i1 #print ("FindLCZ", tx_freq, i1, i2, self.TunerLCZ[index]) return self.TunerLCZ[index] def SetTxFreq(self, tx_freq, no_tune=False): self.tx_freq = tx_freq if tx_freq is None: return if tx_freq < 17000000: self.antnum = 0 else: self.antnum = 1 if self.antnum == 1: self.dipole2.SetTxFreq(tx_freq) if not self.socket: return if no_tune: # Change dipole but don't tune the antenna tuner return if abs(self.tune_freq - tx_freq) < 5000: # ignore small changes return self.tune_freq = tx_freq f, newL, newC, newH = self.FindLCZ(tx_freq) if newH != self.set_HighZ or self.set_C != newC or self.set_L != newL: self.set_HighZ = newH self.set_C = newC self.set_L = newL self.WantData() def HeartBeat(self): global gatewayTime if not self.socket: return try: # The tuner echoes its command self.have_data = data = self.socket.recv(50) except socket.error: data = '' except socket.timeout: data = '' if self.have_data != self.want_data and time.time() - gatewayTime > gatewayLimit: gatewayTime = time.time() self.Send() self.timer += 1 if self.timer == 10: print ('Antenna tuner error') self.timer = 0 else: self.timer = 0 self.dipole2.HeartBeat() def Send(self): try: self.socket.send(self.want_data) except socket.error: pass except socket.timeout: pass class StationControlGUI(wx.Frame): # Display a control window for my antenna tuner def __init__(self, frame, hware, app, conf): wx.Frame.__init__(self, parent=frame, title="Station Control", style=wx.CAPTION|wx.CLOSE_BOX) self.hware = hware self.application = app self.conf = conf self.data_saving = False self.tx_freq = 0 if sys.platform == "win32": self.filename = 'C:/pub/TunerLCZ.tmp' else: self.filename = '/home/jim/pub/TunerLCZ.tmp' fp = open(self.filename, "r") lines = fp.readlines() fp.close() self.TunerLCZ = [(0, 0, 0, 0), (999111000, 0, 0, 0)] # Add dummy first and last entry. for line in lines: freq, antL, antC, hilo = line.split() freq = int(freq) antL = int(antL) antC = int(antC) hilo = int(hilo) self.TunerLCZ.append((freq, antL, antC, hilo)) self.TunerLCZ.sort() self.SetBackgroundColour('light steel blue') sizer = wx.GridBagSizer(hgap=5, vgap=3) font = wx.Font(12, wx.FONTFAMILY_SWISS, wx.NORMAL, wx.FONTWEIGHT_NORMAL, False, conf.quisk_typeface) row = 0 szr = wx.BoxSizer(wx.HORIZONTAL) self.bands = [] for band in ('160', '80', '60', '40', '30', '20', '17', '15', '12', '10'): b = wx.lib.buttons.GenToggleButton(self, -1, band) b.SetUseFocusIndicator(False) b.SetBezelWidth(4) self.bands.append(b) szr.Add(b) self.Bind(wx.EVT_BUTTON, self.OnBtnBand, b) sizer.Add(szr, pos=(row, 0), span=(1, 3), flag=wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT|wx.TOP, border=20) row = 1 b = wx.StaticText(self, -1, " Tuner Adjust L ", style=wx.ALIGN_CENTER) b.SetFont(font) sizer.Add(b, pos=(row, 0), flag=wx.ALIGN_CENTER_VERTICAL) b = self.sliderL = wx.Slider(self, -1, 0, 0, 255, size=(700, -1), style=wx.SL_HORIZONTAL|wx.SL_LABELS) b.Bind(wx.EVT_SCROLL, self.OnSliderL) sizer.Add(b, pos=(row,1)) b = self.btnHiLo = wx.lib.buttons.GenToggleButton(self, -1, "HiLo") b.SetUseFocusIndicator(False) b.SetBezelWidth(4) b.SetFont(font) self.Bind(wx.EVT_BUTTON, self.OnButtonHiLo, b) sizer.Add(b, pos=(row,2), flag=wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, border=20) row = 2 b = wx.StaticText(self, -1, " Tuner Adjust C ") b.SetFont(font) sizer.Add(b, pos=(row, 0), flag=wx.ALIGN_CENTER_VERTICAL) b = self.sliderC = wx.Slider(self, -1, 0, 0, 255, size=(700, -1), style=wx.SL_HORIZONTAL|wx.SL_LABELS) b.Bind(wx.EVT_SCROLL, self.OnSliderC) sizer.Add(b, pos=(row,1), flag=wx.ALIGN_CENTER_VERTICAL) b = wx.lib.buttons.GenButton(self, -1, "Save") b.SetUseFocusIndicator(False) b.SetBezelWidth(4) b.SetFont(font) self.Bind(wx.EVT_BUTTON, self.OnButtonSave, b) sizer.Add(b, pos=(row,2), flag=wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, border=20) row = 3 szr = wx.BoxSizer(wx.HORIZONTAL) szr.AddSpacer(20) b = wx.lib.buttons.GenToggleButton(self, -1, "Key Down") b.SetFont(font) b.SetUseFocusIndicator(False) b.SetBezelWidth(4) self.Bind(wx.EVT_BUTTON, self.OnBtnKey, b) szr.Add(b) szr.AddSpacer(10) b = wx.lib.buttons.GenToggleButton(self, -1, "FilterV2 Preamp") b.SetFont(font) b.SetUseFocusIndicator(False) b.SetBezelWidth(4) if conf.use_rx_udp == 10: # Hermes UDP protocol for Hermes-Lite2 b.Enable(False) self.Bind(wx.EVT_BUTTON, self.OnBtnV2Preamp, b) szr.Add(b) self.LCZtext = wx.StaticText(self, -1, " L 00 C 00 High ", style=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT) self.LCZtext.SetFont(font) szr.Add(self.LCZtext) sizer.Add(szr, pos=(row, 0), span=(1, 3)) #sizer.Add(self.LCZtext, pos=(row, 4), span=(1, 3)) self.SetSizer(sizer) self.Fit() w, h = self.GetSize().Get() self.SetSize((w, h + 20)) self.Bind(wx.EVT_CLOSE, self.OnBtnClose) if frame is None: # Start a HeartBeat if the frame does not provide one self.timer = wx.Timer(self) self.Bind(wx.EVT_TIMER, self.HeartBeat) self.timer.Start(100) def SetTxFreq(self, tx_freq): self.tx_freq = tx_freq index, tup = self.FindIndexTup(tx_freq) self.LCZtext.SetLabel(" Freq %8d, L %2d, C %2d, HiLo %d" % tup) anttuner = self.hware.anttuner fff, anttuner.set_L, anttuner.set_C, anttuner.set_HighZ = tup if tx_freq < 17000000: anttuner.antnum = 0 else: anttuner.antnum = 1 if anttuner.antnum == 1: anttuner.dipole2.SetTxFreq(tx_freq) anttuner.WantData() def FindIndexTup(self, tx_freq): i1 = 0 i2 = len(self.TunerLCZ) - 1 while 1: # binary partition i = (i1 + i2) // 2 if self.TunerLCZ[i][0] < tx_freq: i1 = i else: i2 = i if i2 - i1 <= 1: break # The correct setting is between i1 and i2. if tx_freq == self.TunerLCZ[i2][0]: index = i2 else: index = i1 #print ("FindIndexTup", tx_freq, i1, i2, self.TunerLCZ[index]) return index, self.TunerLCZ[index] def OnBtnClose(self, event): self.hware.anttuner.close() self.hware.controlbox.close() self.Destroy() def HeartBeat(self, event): self.hware.anttuner.HeartBeat() self.hware.controlbox.HeartBeat() if self.hware.v2filter: self.hware.v2filter.HeartBeat() def Open(self): # Initialize the hardware self.bands[0].SetToggle(True) # 160 meters self.hware.anttuner.open() if self.hware.v2filter: self.hware.v2filter.SetTxFreq(1900000) # Start 160 meters def OnBtnBand(self, event): btn = event.GetEventObject() for band in self.bands: if band == btn: band.SetToggle(True) else: band.SetToggle(False) #band = btn.GetLabel() #f1, f2 = self.conf.BandEdge[band] #f = (f1 + f2) / 2 #if self.hware.v2filter: # self.hware.v2filter.SetTxFreq(f) #self.hware.anttuner.SetTxFreq(f, no_tune=True) def OnButtonHiLo(self, event): anttuner = self.hware.anttuner if self.btnHiLo.GetValue(): anttuner.set_HighZ = 1 else: anttuner.set_HighZ = 0 anttuner.WantData() def OnButtonSave(self, event): L = self.sliderL.GetValue() C = self.sliderC.GetValue() if self.btnHiLo.GetValue(): hilo = 1 else: hilo = 0 tup = (self.tx_freq, L, C, hilo) index, t = self.FindIndexTup(self.tx_freq) freq = self.TunerLCZ[index][0] if self.tx_freq < freq: self.TunerLCZ.insert(0, tup) elif self.tx_freq == freq: self.TunerLCZ[index] = tup elif index + 1 < len(self.TunerLCZ) and self.tx_freq == self.TunerLCZ[index + 1][0]: self.TunerLCZ[index + 1] = tup else: self.TunerLCZ.insert(index + 1, tup) self.SetTxFreq(self.tx_freq) #for tup in self.TunerLCZ: # print("New %12d %2d %2d %d" % tup) fp = open(self.filename, 'w') for tup in self.TunerLCZ[1:-1]: # Throw away dummy entries fp.write("%12d %2d %2d %d\n" % tup) fp.close() def OnSliderL(self, event): self.hware.anttuner.set_L = event.GetEventObject().GetValue() self.hware.anttuner.WantData() def OnSliderC(self, event): self.hware.anttuner.set_C = event.GetEventObject().GetValue() self.hware.anttuner.WantData() def OnBtnV2Preamp(self, event): if self.hware.v2filter: self.hware.v2filter.SetPreamp(event.GetEventObject().GetValue()) def OnBtnKey(self, event): self.hware.controlbox.SetKeyDown(event.GetEventObject().GetValue()) class AT200PC: # Control an AT-200PC autotuner made by LDG def __init__(self, app, conf): import serial self.application = app # Application instance (to provide attributes) self.conf = conf # Config file module self.serial = None self.rx_state = 0 self.is_standby = None self.tx_freq = 0 self.old_tx_freq = 0 self.set_L = -9 self.set_C = -9 self.set_HiLoZ = -9 self.tuning_F1 = 0 self.tuning_F2 = 0 self.tuning_diff = 0 self.param1 = [None] * 20 # Parameters returned by the AT-200PC self.param2 = [None] * 20 self.param1[5] = self.param2[5] = self.param2[6] = 0 # power and SWR self.param1[7] = self.param2[7] = 1 # Frequency self.param1[1] = self.param1[2] = 0 # Inductor, Capacitor self.req_swr = 50 # Requested SWR: 50 thru 56 for 1.1, 1.3, 1.5, 1.7, 2.0, 2.5, 3.0 self.live_update = 0 # Request live update 1 or 0 self.antenna = 2 # Select antenna 1 or 2 self.standby = 0 # Set standby mode 1 or 0 self.timer = 0 self.error = "AT-200PC serial port is not open" self.TunerLC_change = False if sys.platform == "win32": self.TunerLC_fname = 'C:/pub/TunerLC.txt' else: self.TunerLC_fname = '/home/jim/pub/TunerLC.txt' def UpdateSwr(self): if not self.application.bottom_widgets: return if self.error: self.application.bottom_widgets.UpdateText(self.error) else: power = (self.param1[5] * 256 + self.param2[5]) / 100.0 swr = self.param2[6] # swr code = 256 * p**2 if power >= 2.0: freq = self.param1[7] * 256 + self.param2[7] freq = 20480000.0 / freq ftext = "Tx freq" swr = math.sqrt(swr / 256.0) swr = (1.0 + swr) / (1.0 - swr) if swr > 99.9: swr = 99.9 else: freq = self.tuning_diff / 1000.0 ftext = "Tune delta" swr = 0.0 if self.param1[3] == 0: # HiLoZ relay value t = "Zh" # High else: t = "Zl" # Low text = "Watts %.0f SWR %.1f %s Ind %d Cap %d %s %.0f kHz" % ( power, swr, t, self.param1[1], self.param1[2], ftext, freq) self.application.bottom_widgets.UpdateText(text) def HeartBeat(self): if not self.serial: self.UpdateSwr() return self.Read() # Receive from the AT-200PC # Call main application with new SWR data self.UpdateSwr() if self.error: # Send a couple parameters, see if we get a response if self.req_swr - 50 != self.param1[16]: self.Write(chr(self.req_swr)) # Send threshold SWR elif self.param1[17] != 0: self.Write(chr(59)) # Turn off AutoTune else: self.error = '' return if self.param1[4] != self.antenna - 1: # Check correct antenna self.Write(chr(9 + self.antenna)) elif self.is_standby != self.standby: # Check standby state self.Write(chr(45 - self.standby)) elif self.param1[19] != self.live_update: # Check live update state self.Write(chr(64 - self.live_update)) elif self.set_L >= 0 and self.set_HiLoZ >= 0 and ( # Check L and Hi/Lo relay self.param1[1] != self.set_L or self.param1[3] != self.set_HiLoZ): if self.set_HiLoZ: self.Write(chr(65) + chr(self.set_L + 128)) else: self.Write(chr(65) + chr(self.set_L)) elif self.param1[2] != self.set_C and self.set_C >= 0: # Set C self.Write(chr(66) + chr(self.set_C)) elif self.live_update: # If our window shows, request an update self.timer += 1 if self.timer > 20: self.timer = 0 self.Write(chr(40)) # Request ALLUPDATE def Write(self, s): # Write a command string to the AT-200PC if DEBUG: print ('Send', ord(s[0])) if self.serial: self.serial.setRTS(1) # Wake up the AT-200PC time.sleep(0.003) # Wait 3 milliseconds self.serial.write(s) self.serial.setRTS(0) def Read(self): # Receive characters from the AT-200PC chars = self.serial.read(1024) for ch in chars: if self.rx_state == 0: # Read first of 4 characters; must be decimal 165 if ord(ch) == 165: self.rx_state = 1 elif self.rx_state == 1: # Read second byte self.rx_state = 2 self.rx_byte1 = ord(ch) elif self.rx_state == 2: # Read third byte self.rx_state = 3 self.rx_byte2 = ord(ch) elif self.rx_state == 3: # Read fourth byte self.rx_state = 0 byte3 = ord(ch) byte1 = self.rx_byte1 byte2 = self.rx_byte2 if DEBUG: print ('Received', byte1, byte2, byte3) if byte1 > 19: # Impossible command value continue if byte1 == 1 and self.set_L < 0: # reported inductor value self.set_L = byte2 if byte1 == 2 and self.set_C < 0: # reported capacitor value self.set_C = byte2 if byte1 == 3 and self.set_HiLoZ < 0: # reported Hi/Lo relay self.set_HiLoZ = byte2 if byte1 == 13: # Start standby self.is_standby = 1 elif byte1 == 14: # Start active self.is_standby = 0 self.param1[byte1] = byte2 self.param2[byte1] = byte3 def OpenPort(self): if sys.platform == "win32": tty_list = ("COM7", "COM8", "COM10", "COM11") else: tty_list = ("/dev/ttyUSB0", "/dev/ttyUSB1", "/dev/ttyUSB2") for tty_name in tty_list: try: port = serial.Serial(port=tty_name, baudrate=9600, timeout=0) except: #traceback.print_exc() pass else: port.setRTS(0) time.sleep(0.1) for i in range(3): port.setRTS(1) time.sleep(0.003) # Wait 3 milliseconds port.write(chr(41)) port.setRTS(0) time.sleep(0.1) chars = port.read(1024) #print 'Got', tty_name, len(chars), repr(chars) if chars == '?\r\n': # Wrong port break if "\xA5\x0B\x01\x20" in chars: self.serial = port break if self.serial: break else: port.close() def open(self): self.OpenPort() if self.serial: self.error = "Waiting for AT200PC" # TunerLC is a list of (freq, L, C). Use -L for Low Z, +L for High Z. # The first and last entry must have frequency 0 and 99999999. self.TunerLC = [] fp = open(self.TunerLC_fname, 'r') for line in fp: line = line.split() f = int(line[0]) l = int(line[1]) c = int(line[2]) self.TunerLC.append((f, l, c)) fp.close() def close(self): if self.serial: self.serial.close() self.serial = None if self.TunerLC_change: fp = open(self.TunerLC_fname, 'w') for f, l, c in self.TunerLC: fp.write("%9d %4d %4d\n" % (f, l, c)) fp.close() def xxReqSetFreq(self, tx_freq): # Set relays for this frequency. The frequency must exist in the tuner. if self.serial and not self.standby and tx_freq > 1500000: ticks = int(20480.0 / tx_freq * 1e6 + 0.5) self.Write(chr(67) + chr((ticks & 0xFF00) >> 8) + chr(ticks & 0xFF)) def SetTxFreq(self, tx_freq): if tx_freq is None: self.set_C = 0 self.set_L = 0 self.set_HiLoZ = 0 return self.tx_freq = tx_freq if abs(self.old_tx_freq - tx_freq) < 20000: d1 = tx_freq - self.tuning_F1 d2 = tx_freq - self.tuning_F2 if abs(d1) <= abs(d2): self.tuning_diff = d1 else: self.tuning_diff = d2 return # Ignore small tuning changes self.old_tx_freq = tx_freq i1 = 0 i2 = len(self.TunerLC) - 1 while 1: # binary partition i = (i1 + i2) // 2 if self.TunerLC[i][0] < tx_freq: i1 = i else: i2 = i if i2 - i1 <= 1: break # The correct setting is between i1 and i2; interpolate F1 = self.TunerLC[i1][0] F2 = self.TunerLC[i2][0] L1 = self.TunerLC[i1][1] L2 = self.TunerLC[i2][1] C1 = self.TunerLC[i1][2] C2 = self.TunerLC[i2][2] frac = (float(tx_freq) - F1) / (F2 - F1) C = C1 + (C2 - C1) * frac self.set_C = int(C + 0.5) L = L1 + (L2 - L1) * frac if L < 0: L = -L self.set_HiLoZ = 1 else: self.set_HiLoZ = 0 self.set_L = int(L + 0.5) # Report the frequency difference self.tuning_F1 = F1 self.tuning_F2 = F2 d1 = tx_freq - F1 d2 = tx_freq - F2 if abs(d1) <= abs(d2): self.tuning_diff = d1 else: self.tuning_diff = d2 def ChangeBand(self, band): pass ##self.ReqSetFreq(self.tx_freq) def OnSpot(self, level): # level is -1 for Spot button Off; else the Spot level 0 to 1000. if self.serial: if level < 0: self.live_update = 0 elif not self.live_update: self.live_update = 1 self.timer = 999 def OnAntTuner(self, text): # One of the tuner buttons was pressed if self.serial: if text == 'Tune': if not self.standby: #self.Write(chr(5)) # Request memory tune self.Write(chr(6)) # Request full tune self.set_C = -9 self.set_L = -9 self.set_HiLoZ = -9 elif text == 'Save': self.Write(chr(46)) if self.set_HiLoZ == 0: # High Z L = self.set_L else: # Low Z L = -self.set_L for i in range(len(self.TunerLC)): # Record new freq and L/C if abs(self.TunerLC[i][0] - self.tx_freq) < 1000: self.TunerLC[i] = (self.tx_freq, L, self.set_C) break else: self.TunerLC.append((self.tx_freq, L, self.set_C)) self.TunerLC.sort() self.TunerLC_change = True elif text == 'L+': self.set_L += 1 elif text == 'L-': self.set_L -= 1 elif text == 'C+': self.set_C += 1 elif text == 'C-': self.set_C -= 1 class App(wx.App): def OnInit(self): if sys.path[0] != "'.'": # Make sure the current working directory is on path sys.path.insert(0, '.') import quisk_conf_defaults as conf import quisk_hardware_model as hardware self.bottom_widgets = None hardware.anttuner = AntennaTuner(self, conf) # Control the antenna tuner hardware.v2filter = FilterBoxV2(self, conf) # Control V2 filter box hardware.controlbox = ControlBox(self, conf) # Control my Station Control Box self.main_frame = frame = StationControlGUI (None, hardware, self, conf) frame.Open() frame.Show() return True if __name__ == '__main__': App().MainLoop()