#---------------------------------------------------- SpecJT from Tkinter import * from tkMessageBox import showwarning import time import os import Pmw import smeter import Audio import g import string import cPickle #import Numeric from Numeric import zeros, multiarray import Image, ImageTk, ImageDraw from palettes import colormapblue, colormapgray0, colormapHot, \ colormapAFMHot, colormapgray1, colormapLinrad, Colormap2Palette #import wsjt #Is this OK to do? def hidespecjt(): root.withdraw() def showspecjt(): root.deiconify() if(__name__=="__main__"): root = Tk() else: root=Toplevel() root.protocol('WM_DELETE_WINDOW',hidespecjt) root.withdraw() #------------------------------------------- Define globals and initialize. b0=0 c0=0 g0=0 g.cmap="Linrad" df=2.69165 isec0=-99 logmap=IntVar() logmap.set(0) logm0=0 minsep=IntVar() mode0="" mousedf0=0 naxis=IntVar() ncall=0 newMinute=0 nflat=IntVar() nfreeze0=0 nmark=IntVar() nmark0=0 nn=0 npal=IntVar() npal.set(2) nscroll=0 nspeed0=IntVar() nspeed00=99 root_geom="" t0="" tol0=400 ttot=0.0 c=Canvas() a=zeros(225000,'s') im=Image.new('P',(750,300)) line0=Image.new('P',(750,1)) #Image fragment for top line of waterfall draw=ImageDraw.Draw(im) pim=ImageTk.PhotoImage(im) balloon=Pmw.Balloon(root) def pal_gray0(): g.cmap="gray0" im.putpalette(Colormap2Palette(colormapgray0),"RGB") def pal_gray1(): g.cmap="gray1" im.putpalette(Colormap2Palette(colormapgray1),"RGB") def pal_linrad(): g.cmap="Linrad" im.putpalette(Colormap2Palette(colormapLinrad),"RGB") def pal_blue(): g.cmap="blue" im.putpalette(Colormap2Palette(colormapblue),"RGB") def pal_Hot(): g.cmap="Hot" im.putpalette(Colormap2Palette(colormapHot),"RGB") def pal_AFMHot(): g.cmap="AFMHot" im.putpalette(Colormap2Palette(colormapAFMHot),"RGB") #--------------------------------------------------- Command button routines #--------------------------------------------------- rx_volume def rx_volume(): for path in string.split(os.environ["PATH"], os.pathsep): file = os.path.join(path, "sndvol32") + ".exe" try: return os.spawnv(os.P_NOWAIT, file, (file,) + (" -r",)) except os.error: pass raise os.error, "Cannot find "+file #--------------------------------------------------- tx_volume def tx_volume(): for path in string.split(os.environ["PATH"], os.pathsep): file = os.path.join(path, "sndvol32") + ".exe" try: return os.spawnv(os.P_NOWAIT, file, (file,)) except os.error: pass raise os.error, "Cannot find "+file #---------------------------------------------------- fdf_change # Readout of graphical cursor location def fdf_change(event): if nspeed0.get()<6: g.DFreq=df*(event.x-288.7) g.Freq=g.DFreq+1270.46 t="Freq: %5d DF: %5d (Hz)" % (int(g.Freq),int(g.DFreq)) else: g.PingTime=0.04*event.x g.PingFreq=(121-event.y)*21.533 g.PingFile="current" if event.y > 150: g.PingFile="previous" g.PingFreq=(271-event.y)*21.533 if g.PingFreq<400: g.PingFreq=0 t="Time: %4.1f s %s Freq: %d Hz" % (g.PingTime,g.PingFile,g.PingFreq) fdf.configure(text=t) #---------------------------------------------------- set_freezedf def set_freezedf(event): if g.mode[:4]=='JT65': n=int(df*(event.x-288.7)) if n<-600: n=-600 if n>600: n=600 Audio.gcom2.mousedf=n else: decode_request(event) #------------------------------------------------------ ftnstr def ftnstr(x): y="" for i in range(len(x)): y=y+x[i] return y #---------------------------------------------------- df_mark def df_mark(): draw_axis() if nmark.get()==0 or Audio.gcom2.nfreeze: fstep=10.0*11025.0/4096.0 if g.mode[4:5]=='B': fstep=2*fstep if g.mode[4:5]=='C': fstep=4*fstep # Mark sync tone and top JT65 tone (green) and shorthand tones (red) color='green' x1=(Audio.gcom2.mousedf + 6.6*fstep)/df + 288.7 c.create_line(x1-0.5,25,x1-0.5,12,fill=color) c.create_line(x1+0.5,25,x1+0.5,12,fill=color) for i in range(5): x1=(Audio.gcom2.mousedf+i*fstep)/df + 288.7 j=12 if i>0: j=15 if i!=1: c.create_line(x1-0.5,25,x1-0.5,j,fill=color) if i!=1: c.create_line(x1+0.5,25,x1+0.5,j,fill=color) color='red' #---------------------------------------------------- decode_request def decode_request(event): if g.mode[:4]!='JT65' and nspeed0.get()>5: # If decoder is busy or we are not monitoring, ignore request if Audio.gcom2.ndecoding==0 and Audio.gcom2.monitoring: Audio.gcom2.mousebutton=event.num #Left=1, Right=3 Audio.gcom2.npingtime=int(40*event.x) #Time (ms) of selected ping if event.y <= 150: Audio.gcom2.ndecoding=2 #Mouse pick, top half else: Audio.gcom2.ndecoding=3 #Mouse pick, bottom half #---------------------------------------------------- freeze_decode def freeze_decode(event): if g.mode[:4]=='JT65' and nspeed0.get()<6: # If decoder is busy or we are not monitoring, ignore request if Audio.gcom2.ndecoding==0 or Audio.gcom2.monitoring==0: set_freezedf(event) g.freeze_decode=1 #---------------------------------------------------- update def update(): global a,b0,c0,g0,im,isec0,line0,newMinute,nscroll,nspeed00,pim, \ root_geom,t0,mousedf0,nfreeze0,tol0,mode0,nmark0,logm0 utc=time.gmtime(time.time()+0.1*Audio.gcom1.ndsec) isec=utc[5] if isec != isec0: #Do once per second isec0=isec t0=time.strftime('%H:%M:%S',utc) ltime.configure(text=t0) root_geom=root.geometry() g.rms=Audio.gcom1.rms if isec==0: nscroll=0 if isec==59: newMinute=1 if g.showspecjt: showspecjt() g.showspecjt=0 nspeed=nspeed0.get() #Waterfall update rate if (nspeed<6 and nspeed00>=6) or (nspeed>=6 and nspeed00<6): draw_axis() nspeed00=nspeed brightness=sc1.get() contrast=sc2.get() logm=logmap.get() g0=sc3.get() if Audio.gcom2.monitoring or Audio.gcom2.ndiskdat: Audio.spec(brightness,contrast,logm,g0,nspeed,a) #Call Fortran routine spec newdat=Audio.gcom1.newdat #True if new data available else: newdat=0 sm.updateProgress(newValue=Audio.gcom1.level) #S-meter bar if newdat or brightness!=b0 or contrast!=c0 or logm!=logm0: if brightness==b0 and contrast==c0 and logm==logm0 and nspeed<6: n=Audio.gcom2.nlines box=(0,0,750,300-n) #Define region region=im.crop(box) #Get all but last line(s) im.paste(region,(0,n)) #Move waterfall down for i in range(n): line0.putdata(a[750*i:750*(i+1)]) #One row of pixels to line0 im.paste(line0,(0,i)) #Paste in new top line nscroll=nscroll+n else: #A scale factor has changed im.putdata(a) #Compute whole new image b0=brightness #Save scale values c0=contrast logm0=logm if newdat: if Audio.gcom2.monitoring: if minsep.get() and newMinute: draw.line((0,0,749,0),fill=128) #Draw the minute separator if nscroll == 13: draw.text((5,2),t0[0:5],fill=253) #Insert time label else: if minsep.get(): draw.line((0,0,749,0),fill=128) #Draw the minute separator # Don't update waterfall while decoding pim=ImageTk.PhotoImage(im) #Convert Image to PhotoImage graph1.delete(ALL) #For some reason, top two lines are invisible, so we move down 2 graph1.create_image(0,0+2,anchor='nw',image=pim) if nspeed>5: color="white" if g.cmap=="gray1": color="black" t=time.strftime("%H:%M:%S",time.gmtime(Audio.gcom2.ntime + \ 0.1*Audio.gcom1.ndsec)) graph1.create_text(5,110,anchor=W,text=t,fill=color) t=g.filetime(g.ftnstr(Audio.gcom2.fnamea)) graph1.create_text(5,260,anchor=W,text=t,fill=color) newMinute=0 if (Audio.gcom2.mousedf != mousedf0 or Audio.gcom2.dftolerance != tol0) \ and g.mode[:4]=='JT65': df_mark() # The following int() calls are to ensure that the values copied to # mousedf0 and tol0 are static. mousedf0=int(Audio.gcom2.mousedf) tol0=int(Audio.gcom2.dftolerance) if Audio.gcom2.nfreeze != nfreeze0: if not Audio.gcom2.nfreeze: draw_axis() if Audio.gcom2.nfreeze: df_mark() nfreeze0=int(Audio.gcom2.nfreeze) if g.mode!=mode0: draw_axis() mode0=g.mode if nmark.get()!=nmark0: df_mark() nmark0=nmark.get() if newdat: Audio.gcom2.ndiskdat=0 Audio.gcom2.nlines=0 Audio.gcom2.nflat=nflat.get() ltime.after(200,update) #Reset the timer #-------------------------------------------------------- draw_axis def draw_axis(): xmid=1500 if naxis.get(): xmid=xmid-1270.46 c.delete(ALL) if nspeed0.get()<6: for ix in range(-1000,2501,20): i=374.5 + (ix-xmid)/df j=20 if (ix%100)==0 : j=16 x=i-2 if ix<1000 : x=x+2 y=8 c.create_text(x,y,text=str(ix)) c.create_line(i,25,i,j,fill='black') if g.mode[:4]=="JT65": if Audio.gcom2.nfreeze==0: x1=-600/df + 288.7 x2=600/df + 288.7 else: tol=Audio.gcom2.dftolerance x1=(Audio.gcom2.mousedf-tol)/df + 288.7 x2=(Audio.gcom2.mousedf+tol)/df + 288.7 c.create_line(x1,25,x2,25,fill='green',width=2) else: for ix in range(1,31): i=25*ix j=20 if (ix%5)==0: j=16 x=i if x==750: x=745 c.create_text(x,8,text=str(ix)) c.create_line(i,25,i,j,fill='black') #-------------------------------------------------------- Create GUI widgets #-------------------------------------------------------- Menu bar frame = Frame(root) frame.pack() mbar = Frame(frame) mbar.pack(fill=X) #--------------------------------------------------------- Options menu setupbutton = Menubutton(mbar, text = 'Options', ) setupbutton.pack(side = LEFT) setupmenu = Menu(setupbutton, tearoff=1) setupbutton['menu'] = setupmenu setupmenu.add_checkbutton(label = 'Mark T/R boundaries',variable=minsep) setupmenu.add_checkbutton(label='Flatten spectra',variable=nflat) setupmenu.add_checkbutton(label='Mark JT65 tones only if Freeze is checked', variable=nmark) setupmenu.add_separator() setupmenu.add('command', label = 'Rx volume control', command = rx_volume) setupmenu.add('command', label = 'Tx volume control', command = tx_volume) setupmenu.add_separator() setupmenu.add_radiobutton(label='Frequency axis',command=draw_axis, value=0,variable=naxis) setupmenu.add_radiobutton(label='JT65 DF axis',command=draw_axis, value=1,variable=naxis) setupmenu.add_separator() #setupmenu.add_radiobutton(label='High sensitivity',value=0,variable=logmap) #setupmenu.add_radiobutton(label='High dynamic range',value=1,variable=logmap) #setupmenu.add_separator() setupmenu.palettes=Menu(setupmenu,tearoff=0) setupmenu.palettes.add_radiobutton(label='Gray0',command=pal_gray0, value=0,variable=npal) setupmenu.palettes.add_radiobutton(label='Gray1',command=pal_gray1, value=1,variable=npal) setupmenu.palettes.add_radiobutton(label='Linrad',command=pal_linrad, value=2,variable=npal) setupmenu.palettes.add_radiobutton(label='Blue',command=pal_blue, value=3,variable=npal) setupmenu.palettes.add_radiobutton(label='Hot',command=pal_Hot, value=4,variable=npal) setupmenu.palettes.add_radiobutton(label='AFMHot',command=pal_AFMHot, value=5,variable=npal) setupmenu.add_cascade(label = 'Palette',menu=setupmenu.palettes) lab1=Label(mbar,padx=50,bd=0) lab1.pack(side=LEFT) fdf=Label(mbar,width=12,bd=0,padx=90) fdf.pack(side=LEFT) #------------------------------------------------- Speed selection buttons for i in (7, 6, 5, 4, 3, 2, 1): t=str(i) if i==6: t="H1" if i==7: t="H2" Radiobutton(mbar,text=t,value=i,variable=nspeed0).pack(side=RIGHT) nspeed0.set(6) lab2=Label(mbar,text='Speed: ',bd=0) lab2.pack(side=RIGHT) #------------------------------------------------- Graphics frame iframe1 = Frame(frame, bd=1, relief=SUNKEN) c=Canvas(iframe1, bg='white', width=750, height=25,bd=0) c.pack(side=TOP) graph1=Canvas(iframe1, bg='black', width=750, height=300,bd=0,cursor='crosshair') graph1.pack(side=TOP) Widget.bind(graph1,"",fdf_change) #Widget.bind(graph1,"",decode_request) Widget.bind(graph1,"",decode_request) Widget.bind(graph1,"",set_freezedf) Widget.bind(graph1,"",freeze_decode) iframe1.pack(expand=1, fill=X) #-------------------------------------------------- Status frame iframe2 = Frame(frame, bd=1, relief=SUNKEN) status=Pmw.MessageBar(iframe2,entry_width=17,entry_relief=GROOVE) status.pack(side=LEFT) sc1=Scale(iframe2,from_=-100.0,to_=100.0,orient='horizontal', showvalue=0,sliderlength=5) sc1.pack(side=LEFT) sc2=Scale(iframe2,from_=-100.0,to_=100.0,orient='horizontal', showvalue=0,sliderlength=5) sc2.pack(side=LEFT) balloon.bind(sc1,"Brightness", "Brightness") balloon.bind(sc2,"Contrast", "Contrast") balloon.configure(statuscommand=status.helpmessage) ltime=Label(iframe2,bg='black',fg='yellow',width=8,bd=2,font=('Arial',16)) ltime.pack(side=LEFT) msg1=Label(iframe2,padx=2,bd=2,text=" ") msg1.pack(side=LEFT) sc3=Scale(iframe2,from_=-100.0,to_=100.0,orient='horizontal', showvalue=0,sliderlength=5) sc3.pack(side=LEFT) balloon.bind(sc3,"Gain", "Digital Gain") sm=smeter.Smeter(iframe2,fillColor='slateblue',width=150, doLabel=1) sm.frame.pack(side=RIGHT) balloon.bind(sm.frame,"Rx noise level","Rx noise level") iframe2.pack(expand=1, fill=X) #----------------------------------------------- Restore params from INI file try: f=open('WSJT.INI',mode='r') params=f.readlines() except: params="" try: for i in range(len(params)): key,value=params[i].split() if key == 'SpecJTGeometry': root.geometry(value) elif key == 'UpdateInterval': nspeed0.set(value) elif key == 'Brightness': sc1.set(value) elif key == 'Contrast': sc2.set(value) elif key == 'DigitalGain': sc3.set(value) elif key == 'MinuteSeparators': minsep.set(value) elif key == 'AxisLabel': naxis.set(value) elif key == 'MarkTones': nmark.set(value) elif key == 'Flatten': nflat.set(value) elif key == 'LogMap': logmap.set(value) elif key == 'Palette': g.cmap=value else: pass except: print 'Error reading WSJT.INI, continuing with defaults.' print key,value #------------------------------------------------------ Select palette if g.cmap == "gray0": pal_gray0() npal.set(0) if g.cmap == "gray1": pal_gray1() npal.set(1) if g.cmap == "Linrad": pal_linrad() npal.set(2) if g.cmap == "blue": pal_blue() npal.set(3) if g.cmap == "Hot": pal_Hot() npal.set(4) if g.cmap == "AFMHot": pal_AFMHot() npal.set(5) #---------------------------------------------- Display GUI and start mainloop draw_axis() Audio.gcom1.ndevin=g.ndevin.get() Audio.gcom1.ndevout=g.ndevout.get() ndevin=g.ndevin.get() ndevout=g.ndevout.get() Audio.audio_init(ndevin,ndevout) #Start the audio stream ltime.after(200,update) root.deiconify() if g.Win32: root.iconbitmap("wsjt.ico") root.title(' SpecJT by K1JT') if(__name__=="__main__"): Audio.gcom2.monitoring=1 root.mainloop() #-------------------------------------------------- Save user params and quit f=open('WSJT.INI',mode='w') f.write("UpdateInterval " + str(nspeed0.get()) + "\n") f.write("Brightness " + str(b0)+ "\n") f.write("Contrast " + str(c0)+ "\n") f.write("DigitalGain " + str(g0)+ "\n") f.write("MinuteSeparators " + str(minsep.get()) + "\n") f.write("AxisLabel " + str(naxis.get()) + "\n") f.write("MarkTones " + str(nmark.get()) + "\n") f.write("Flatten " + str(nflat.get()) + "\n") f.write("LogMap " + str(logmap.get()) + "\n") f.write("Palette " + g.cmap + "\n") root_geom=root_geom[root_geom.index("+"):] f.write("SpecJTGeometry " + root_geom + "\n") f.close()