WSJT-X/specjt.py
Joe Taylor cbb96ca615 Changed version number to 0.8.
Display "No data" if not receiving packets.
Permanent label at top of Messages window.
Display green tick marks at startup.
Fix display of quick-decode time.


git-svn-id: svn+ssh://svn.code.sf.net/p/wsjt/wsjt/branches/map65@482 ab8295b8-cf94-4d9e-aec4-7959e3be5d79
2007-07-10 00:15:11 +00:00

544 lines
16 KiB
Python

#----------------------------------------------------- SpecJT
from Tkinter import *
from tkMessageBox import showwarning
import time
import os
import Pmw
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()
g.showspecjt=0
def showspecjt():
root.deiconify()
g.showspecjt=2
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
fmid=1500
fmid0=1500
frange=2000
frange0=2000
isec0=-99
mode0=""
mousedf0=0
mousefqso0=0
dftolerance0=500
naxis=IntVar()
ncall=0
newMinute=0
nflat=IntVar()
nfr=IntVar()
nfr.set(1)
nfreeze0=0
nmark=IntVar()
nmark0=0
nn=0
npal=IntVar()
npal.set(2)
nscroll=0
nspeed0=IntVar()
root_geom=""
t0=""
tol0=400
ttot=0.0
c=Canvas()
NX=750
NY=130
a=zeros(NX*NY,'s')
im=Image.new('P',(NX,NY))
line0=Image.new('P',(NX,1)) #Image fragment for top line of waterfall
draw=ImageDraw.Draw(im)
pim=ImageTk.PhotoImage(im)
a2=zeros(NX*NY,'s')
im2=Image.new('P',(NX,NY))
line02=Image.new('P',(NX,1)) #Image fragment for top line of zoomed waterfall
draw2=ImageDraw.Draw(im2)
pim2=ImageTk.PhotoImage(im2)
balloon=Pmw.Balloon(root)
def pal_gray0():
g.cmap="gray0"
im.putpalette(Colormap2Palette(colormapgray0),"RGB")
im2.putpalette(Colormap2Palette(colormapgray0),"RGB")
def pal_gray1():
g.cmap="gray1"
im.putpalette(Colormap2Palette(colormapgray1),"RGB")
im2.putpalette(Colormap2Palette(colormapgray1),"RGB")
def pal_linrad():
g.cmap="Linrad"
im.putpalette(Colormap2Palette(colormapLinrad),"RGB")
im2.putpalette(Colormap2Palette(colormapLinrad),"RGB")
def pal_blue():
g.cmap="blue"
im.putpalette(Colormap2Palette(colormapblue),"RGB")
im2.putpalette(Colormap2Palette(colormapblue),"RGB")
def pal_Hot():
g.cmap="Hot"
im.putpalette(Colormap2Palette(colormapHot),"RGB")
im2.putpalette(Colormap2Palette(colormapHot),"RGB")
def pal_AFMHot():
g.cmap="AFMHot"
im.putpalette(Colormap2Palette(colormapAFMHot),"RGB")
im2.putpalette(Colormap2Palette(colormapAFMHot),"RGB")
#--------------------------------------------------- Command button routines
#---------------------------------------------------- fdf_change
# Readout of graphical cursor location
def fdf_change(event):
df=96.0/750.0
fmid=122.8 # empirical
g.Freq=df*(event.x-375) + fmid
n=int(g.Freq+0.5)
t="%d" % (n,)
if g.fc[n] != "":
t=t + ": " + g.fc[n]
fdf.configure(text=t)
def fdf_change2(event):
g.DFreq=(2200.0/750.0)*(event.x-375)
#---------------------------------------------------- set_fqso
def set_fqso(event):
n=int(g.Freq + 0.5)
Audio.gcom2.mousefqso=n
df_mark()
#---------------------------------------------------- set_freezedf
def set_freezedf(event):
n=int(g.DFreq + 0.5)
Audio.gcom2.mousedf=n
df_mark()
print 'B',n
#------------------------------------------------------ ftnstr
def ftnstr(x):
y=""
for i in range(len(x)):
y=y+x[i]
return y
#---------------------------------------------------- df_mark
def df_mark():
draw_axis()
# Mark QSO freq in top graph
color='green'
df=96.0/750.0
x1=393.0 + (Audio.gcom2.mousefqso-125)/df
c.create_line(x1,25,x1,12,fill=color,width=2)
df=96000.0/32768.0
# Mark sync tone and top JT65 tone (green) and shorthand tones (red)
fstep=20.0*11025.0/4096.0
x1=375.0 + (Audio.gcom2.mousedf + 6.6*fstep)/df
c2.create_line(x1,25,x1,12,fill=color,width=2)
x1=375.0 + (Audio.gcom2.mousedf - Audio.gcom2.dftolerance)/df
x2=375.0 + (Audio.gcom2.mousedf + Audio.gcom2.dftolerance)/df
c2.create_line(x1,25,x2,25,fill=color,width=2)
for i in range(5):
x1=375.0 + (Audio.gcom2.mousedf + i*fstep)/df
j=12
if i>0: j=15
if i!=1: c2.create_line(x1,25,x1,j,fill=color,width=2)
color='red'
#---------------------------------------------------- freq_range
def freq_range(event):
# Move frequency scale left or right in 100 Hz increments
global fmid
if event.num==1:
fmid=fmid+100
else:
if event.num==3:
fmid=fmid-100
if fmid<1000*nfr.get(): fmid=1000*nfr.get()
if fmid>5000-1000*nfr.get(): fmid=5000-1000*nfr.get()
#---------------------------------------------------- freeze_decode1
def freeze_decode1(event):
# If decoder is busy or we are not monitoring, ignore request
if Audio.gcom2.ndecoding==0 or Audio.gcom2.monitoring==0:
set_fqso(event)
g.freeze_decode=1
#---------------------------------------------------- freeze_decode2
def freeze_decode2(event):
# 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,a2,b0,c0,g0,im,im2,isec0,line0,line02,newMinute,\
nscroll,pim,pim2, \
root_geom,t0,mousedf0,mousefqso0,nfreeze0,tol0,mode0,nmark0, \
fmid,fmid0,frange,frange0,dftolerance0
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==1:
showspecjt()
nspeed=nspeed0.get() #Waterfall update rate
brightness=sc1.get()
contrast=sc2.get()
g0=sc3.get()
newspec=Audio.gcom2.newspec #True if new data available
if newspec:
Audio.spec(brightness,contrast,g0,nspeed,a,a2) #Call Fortran routine spec
n=Audio.gcom2.nlines
box=(0,0,NX,130-n) #Define region
region=im.crop(box) #Get all but last line(s)
region2=im2.crop(box) #Get all but last line(s)
box=(125,0,624,120)
try:
im.paste(region,(0,n)) #Move waterfall down
im2.paste(region2,(0,n)) #Move waterfall down
except:
print "Images did not match, continuing anyway."
for i in range(n):
line0.putdata(a[NX*i:NX*(i+1)]) #One row of pixels to line0
im.paste(line0,(0,i)) #Paste in new top line(s)
line02.putdata(a2[NX*i:NX*(i+1)])#One row of pixels to line0
im2.paste(line02,(0,i)) #Paste in new top line(s)
nscroll=nscroll+n
b0=brightness #Save scale values
c0=contrast
if newspec:
if Audio.gcom2.monitoring:
if newMinute:
draw.line((0,0,749,0),fill=128) #Draw the minute separator
draw2.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
# draw2.text((5,2),t0[0:5],fill=253) #Insert time label
else:
draw.line((0,0,749,0),fill=128) #Draw the minute separator
draw2.line((0,0,749,0),fill=128) #Draw the minute separator
t=g.ftnstr(Audio.gcom2.utcdata)
# This test shouldn.t be needed, but ...
try:
draw.text((4,1),t[0:5],fill=253) #Insert time label
draw2.text((4,1),t[0:5],fill=253) #Insert time label
except:
pass
pim=ImageTk.PhotoImage(im) #Convert Image to PhotoImage
graph1.delete(ALL)
pim2=ImageTk.PhotoImage(im2) #Convert Image to PhotoImage
graph2.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)
graph2.create_image(0,0+2,anchor='nw',image=pim2)
g.ndecphase=2
newMinute=0
Audio.gcom2.newspec=0
if (Audio.gcom2.mousedf != mousedf0 or
Audio.gcom2.mousefqso != mousefqso0 or
Audio.gcom2.dftolerance != dftolerance0):
df_mark()
# The following int() calls are to ensure that the values copied to
# mousedf0 and mousefqso0 are static.
mousedf0=int(Audio.gcom2.mousedf)
mousefqso0=int(Audio.gcom2.mousefqso)
dftolerance0=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:
df_mark() ### was draw_axis()
mode0=g.mode
if nmark.get()!=nmark0:
df_mark()
nmark0=nmark.get()
# if newspec: Audio.gcom2.ndiskdat=0
Audio.gcom2.nlines=0
Audio.gcom2.nflat=nflat.get()
frange=nfr.get()*2000
if(fmid<>fmid0 or frange<>frange0):
if fmid<1000*nfr.get(): fmid=1000*nfr.get()
if fmid>5000-1000*nfr.get(): fmid=5000-1000*nfr.get()
df_mark()
fmid0=fmid
frange0=frange
Audio.gcom2.nfmid=int(fmid)
Audio.gcom2.nfrange=int(frange)
ltime.after(200,update) #Reset the timer
#-------------------------------------------------------- draw_axis
def draw_axis():
c.delete(ALL)
xmid=125.0 - 2.3 #Empirical
bw=96.0
x1=int(xmid-0.6*bw)
x2=int(xmid+0.6*bw)
xdf=bw/NX #128 Hz
for ix in range(x1,x2,1):
i=0.5*NX + (ix-xmid)/xdf
j=20
if (ix%5)==0: j=16
if (ix%10)==0 :
j=16
x=i-1
if ix<100: x=x+1
y=8
c.create_text(x,y,text=str(ix))
c.create_line(i,25,i,j,fill='black')
c2.delete(ALL)
xmid2=0
bw2=750.0*96000.0/32768.0 #approx 2197.27 Hz
x1=int(xmid-0.5*bw2)/100 - 1
x1=100*x1
x2=int(xmid+0.5*bw2)
xdf2=bw2/NX
for ix in range(x1,x2,20):
i=0.5*NX + (ix-xmid2)/xdf2
j=20
# if (ix%5)==0: j=16
if (ix%100)==0 :
j=16
x=i-1
if ix<1000: x=x+2
if ix<0: x=x-2
y=8
c2.create_text(x,y,text=str(ix))
c2.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)
#--------------------------------------------------------- Palette menu
setupbutton = Menubutton(mbar, text = 'Palette', )
setupbutton.pack(side = LEFT)
setupmenu = Menu(setupbutton,tearoff=0)
setupbutton['menu'] = setupmenu
setupmenu.add_radiobutton(label='Gray0',command=pal_gray0,
value=0,variable=npal)
setupmenu.add_radiobutton(label='Gray1',command=pal_gray1,
value=1,variable=npal)
setupmenu.add_radiobutton(label='Linrad',command=pal_linrad,
value=2,variable=npal)
setupmenu.add_radiobutton(label='Blue',command=pal_blue,
value=3,variable=npal)
setupmenu.add_radiobutton(label='Hot',command=pal_Hot,
value=4,variable=npal)
setupmenu.add_radiobutton(label='AFMHot',command=pal_AFMHot,
value=5,variable=npal)
lab1=Label(mbar,width=20,padx=20,bd=0)
lab1.pack(side=LEFT)
fdf=Label(mbar,width=30,bd=0,anchor=W)
fdf.pack(side=LEFT)
fdf2=Label(mbar,width=15,bd=0)
fdf2.pack(side=LEFT)
lab3=Label(mbar,padx=13,bd=0)
lab3.pack(side=LEFT)
lab0=Label(mbar,padx=10,bd=0)
lab0.pack(side=LEFT)
#------------------------------------------------- Speed selection buttons
for i in (5, 4, 3, 2, 1):
t=str(i)
Radiobutton(mbar,text=t,value=i,variable=nspeed0).pack(side=RIGHT)
nspeed0.set(1)
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=NX, height=25,bd=0)
c.pack(side=TOP)
Widget.bind(c,"<Shift-Button-1>",freq_range)
Widget.bind(c,"<Shift-Button-2>",freq_range)
Widget.bind(c,"<Shift-Button-3>",freq_range)
graph1=Canvas(iframe1, bg='black', width=NX, height=NY,bd=0,cursor='crosshair')
graph1.pack(side=TOP)
Widget.bind(graph1,"<Motion>",fdf_change)
Widget.bind(graph1,"<Button-1>",set_fqso)
Widget.bind(graph1,"<Double-Button-1>",freeze_decode1)
iframe1.pack(expand=1, fill=X)
c2=Canvas(iframe1, bg='white', width=NX, height=25,bd=0)
c2.pack(side=TOP)
Widget.bind(c2,"<Shift-Button-1>",freq_range)
Widget.bind(c2,"<Shift-Button-2>",freq_range)
Widget.bind(c2,"<Shift-Button-3>",freq_range)
graph2=Canvas(iframe1, bg='black', width=NX, height=NY,bd=0,cursor='crosshair')
graph2.pack(side=TOP)
Widget.bind(graph2,"<Motion>",fdf_change2)
Widget.bind(graph2,"<Button-1>",set_freezedf)
Widget.bind(graph2,"<Double-Button-1>",freeze_decode2)
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=('Helvetica',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")
iframe2.pack(expand=1, fill=X)
#----------------------------------------------- Restore params from INI file
try:
f=open('MAP65.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 == 'AxisLabel': naxis.set(value)
elif key == 'MarkTones': nmark.set(value)
elif key == 'Flatten': nflat.set(value)
elif key == 'Palette': g.cmap=value
elif key == 'Frange': nfr.set(value)
elif key == 'Fmid': fmid=int(value)
else: pass
except:
print 'Error reading MAP65.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()
df_mark()
try:
ndevin=g.ndevin.get()
except:
ndevin=0
Audio.gcom1.ndevin=ndevin
try:
ndevout=g.ndevout.get()
except:
ndevout=0
Audio.gcom1.ndevout=ndevout
# Only valid for windows
# for now
Audio.audio_init(ndevin,ndevout) #Start the audio stream
ltime.after(200,update)
root.deiconify()
g.showspecjt=2
if g.Win32: root.iconbitmap("wsjt.ico")
root.title('Waterfall')
if(__name__=="__main__"):
Audio.gcom2.monitoring=1
root.mainloop()
#-------------------------------------------------- Save user params and quit
f=open('MAP65.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("AxisLabel " + str(naxis.get()) + "\n")
f.write("MarkTones " + str(nmark.get()) + "\n")
f.write("Flatten " + str(nflat.get()) + "\n")
f.write("Palette " + g.cmap + "\n")
f.write("Frange " + str(nfr.get()) + "\n")
f.write("Fmid " + str(fmid) + "\n")
root_geom=root_geom[root_geom.index("+"):]
f.write("SpecJTGeometry " + root_geom + "\n")
f.close()