kopia lustrzana https://github.com/ogre/habdec
Added example python client.
rodzic
4ac7295632
commit
ab1386a5d0
|
@ -0,0 +1,9 @@
|
|||
# Example python client for habdec websocket server
|
||||
|
||||
![alt text](./pyClientScreenshot.png)
|
||||
|
||||
This is simple example of python client demonstrating how to connect to habdec server. The code is kept (almost) to minimum and is not meant for anything serius - just a proof of concept.
|
||||
|
||||
You need `pip install ws4py` and numpy.
|
||||
|
||||
GUI is done with tkinter (which you should have with python). Use something better for real work.
|
|
@ -0,0 +1,258 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
# BASIC COMMUNICATION WITH HABDEC WEBSOCKET SERVER
|
||||
#
|
||||
|
||||
import sys
|
||||
import string
|
||||
from ws4py.client.threadedclient import WebSocketClient # pip install ws4py
|
||||
import struct
|
||||
import numpy
|
||||
import time
|
||||
import math
|
||||
import threading
|
||||
import random
|
||||
try:
|
||||
import Tkinter as tk
|
||||
except:
|
||||
import tkinter as tk
|
||||
|
||||
|
||||
# power spectrum as received from server
|
||||
class PowerSpectrum:
|
||||
header_size_ = 0 # int32_t
|
||||
noise_floor_ = 0 # float32
|
||||
noise_variance_ = 0 # float32
|
||||
sampling_rate_= 0 # float32
|
||||
shift_ = 0 # float32
|
||||
peak_left_ = 0 # int32_t
|
||||
peak_right_ = 0 # int32_t
|
||||
peak_left_valid_ = 0 # int32_t
|
||||
peak_right_valid_ = 0 # int32_t
|
||||
min_ = 0 # float32
|
||||
max_ = 0 # float32
|
||||
type_size_ = 0, # int32_t. value:s 1/2/4 for uint8 uint16 float32
|
||||
size_ = 0 # int32_t
|
||||
values_ = [] # raw data of type uint8|uint16|float32
|
||||
|
||||
|
||||
# demodulated graph as received from server
|
||||
class Demod:
|
||||
header_size_ = 0 # int32_t
|
||||
min_ = 0 # float32
|
||||
max_ = 0 # float32
|
||||
type_size_ = 0 # int32_t. value:s 1/2/4 for uint8 uint16 float32
|
||||
size_ = 0 # int32_t
|
||||
values_ = [] # raw data of type uint8|uint16|float32
|
||||
|
||||
|
||||
# GLOBAL VARIABLES:
|
||||
#
|
||||
WS = None # websocket
|
||||
WS_THREAD = None
|
||||
DEMOD = Demod()
|
||||
SPECTRUM = PowerSpectrum()
|
||||
# decoder state
|
||||
STATE = {
|
||||
'frequency': 0,
|
||||
'sampling_rate': 0,
|
||||
'gain': 0,
|
||||
'baud': 0,
|
||||
'rtty_bits': 0,
|
||||
'rtty_stops': 0,
|
||||
'lowpass_bw': 0,
|
||||
'lowpass_trans': 0,
|
||||
'biastee': 0,
|
||||
'decimation': 0,
|
||||
'afc': 0,
|
||||
}
|
||||
SENTENCES = []
|
||||
RTTY_STREAM = ''
|
||||
|
||||
|
||||
# WEBSOCKET CLIENT
|
||||
#
|
||||
class HabDecClient(WebSocketClient):
|
||||
set_callback = None
|
||||
info_callback = None
|
||||
|
||||
def received_message(self, m):
|
||||
i_data = m.data
|
||||
if not i_data:
|
||||
return
|
||||
|
||||
if i_data.startswith('PWR_'): # power spectrum graph data
|
||||
HandleResponse_PowerSpectrum(i_data[len('PWR_'):])
|
||||
elif i_data.startswith('DEM_'): # demodulated graph data
|
||||
HandleResponse_Demod(i_data[len('DEM_'):])
|
||||
elif i_data.startswith('cmd::set:'):
|
||||
HandleResponse_Set(i_data)
|
||||
if self.set_callback:
|
||||
self.set_callback()
|
||||
elif i_data.startswith('cmd::info:'):
|
||||
HandleResponse_Info(i_data)
|
||||
if self.info_callback:
|
||||
self.info_callback()
|
||||
|
||||
def opened(self):
|
||||
print "Opened Connection"
|
||||
|
||||
def closed(self, code, reason=None):
|
||||
print "Closed down", code, reason
|
||||
|
||||
|
||||
def InitConnection(i_srv, set_callback, info_callback):
|
||||
global WS
|
||||
global WS_THREAD
|
||||
WS = HabDecClient( i_srv )
|
||||
WS.set_callback = set_callback
|
||||
WS.info_callback = info_callback
|
||||
WS.connect()
|
||||
WS_THREAD = threading.Thread( target = lambda: WS.run_forever() )
|
||||
WS_THREAD.start()
|
||||
|
||||
|
||||
def CloseConnection():
|
||||
print 'Closing connection...'
|
||||
WS.close()
|
||||
print 'Closed connection...'
|
||||
|
||||
|
||||
def SendCommand(i_cmd):
|
||||
'''
|
||||
available commands:
|
||||
set:frequency=434 get:frequency
|
||||
set:gain=10 get:gain
|
||||
set:baud=50 get:baud
|
||||
set:rtty_bits=8 get:rtty_bits
|
||||
set:rtty_stops=2 get:rtty_stops
|
||||
set:lowpass_bw=.1 get:lowpass_bw
|
||||
set:lowpass_trans=.01 get:lowpass_trans
|
||||
set:biastee=1 get:biastee
|
||||
set:decimation=2 get:decimation
|
||||
set:afc=1 get:afc
|
||||
power:res=512,zoom=0.5
|
||||
demod:res=256
|
||||
liveprint
|
||||
|
||||
For others, see websocket server implementation
|
||||
|
||||
When sending a "set:*' command, server echoes back a value that was actually set.
|
||||
Use it to synchronize your GUI
|
||||
'''
|
||||
if WS:
|
||||
WS.send('cmd::' + i_cmd)
|
||||
|
||||
|
||||
def HandleResponse_Set(i_cmd):
|
||||
global STATE
|
||||
if i_cmd.startswith('cmd::set:'):
|
||||
value = string.split(i_cmd, '=')[-1]
|
||||
if i_cmd.startswith('cmd::set:frequency'):
|
||||
STATE['frequency'] = float(value)
|
||||
elif i_cmd.startswith('cmd::set:gain'):
|
||||
STATE['gain'] = int(float(value))
|
||||
elif i_cmd.startswith('cmd::set:baud'):
|
||||
STATE['baud'] = float(value)
|
||||
elif i_cmd.startswith('cmd::set:rtty_bits'):
|
||||
STATE['rtty_bits'] = int(float(value))
|
||||
elif i_cmd.startswith('cmd::set:rtty_stops'):
|
||||
STATE['rtty_stops'] = int(float(value))
|
||||
elif i_cmd.startswith('cmd::set:lowpass_bw'):
|
||||
STATE['lowpass_bw'] = float(value)
|
||||
elif i_cmd.startswith('cmd::set:lowpass_trans'):
|
||||
STATE['lowpass_trans'] = float(value)
|
||||
elif i_cmd.startswith('cmd::set:biastee'):
|
||||
STATE['biastee'] = bool( float(value) )
|
||||
elif i_cmd.startswith('cmd::set:decimation'):
|
||||
STATE['decimation'] = int(float(value))
|
||||
elif i_cmd.startswith('cmd::set:afc'):
|
||||
STATE['afc'] = bool( float(value) )
|
||||
|
||||
|
||||
def HandleResponse_Info(i_cmd):
|
||||
global STATE
|
||||
if i_cmd.startswith('cmd::info:'):
|
||||
tokens = string.split(i_cmd, '=')
|
||||
value = string.join(tokens[1:], '=')
|
||||
if i_cmd.startswith('cmd::info:sampling_rate'):
|
||||
STATE['sampling_rate'] = float(value)
|
||||
if i_cmd.startswith('cmd::info:liveprint'):
|
||||
global RTTY_STREAM
|
||||
RTTY_STREAM = value
|
||||
if i_cmd.startswith('cmd::info:sentence'):
|
||||
SENTENCES.append(value)
|
||||
|
||||
|
||||
def HandleResponse_PowerSpectrum(_data):
|
||||
global SPECTRUM
|
||||
offset = 0
|
||||
SPECTRUM.header_size_ = struct.unpack('i', _data[offset:offset+4])[0]; offset += 4;
|
||||
SPECTRUM.noise_floor_ = struct.unpack('f', _data[offset:offset+4])[0]; offset += 4;
|
||||
SPECTRUM.noise_variance_ = struct.unpack('f', _data[offset:offset+4])[0]; offset += 4;
|
||||
SPECTRUM.sampling_rate_= struct.unpack('f', _data[offset:offset+4])[0]; offset += 4;
|
||||
SPECTRUM.shift_ = struct.unpack('f', _data[offset:offset+4])[0]; offset += 4;
|
||||
SPECTRUM.peak_left_ = struct.unpack('i', _data[offset:offset+4])[0]; offset += 4;
|
||||
SPECTRUM.peak_right_ = struct.unpack('i', _data[offset:offset+4])[0]; offset += 4;
|
||||
SPECTRUM.peak_left_valid_= struct.unpack('i', _data[offset:offset+4])[0]; offset += 4;
|
||||
SPECTRUM.peak_right_valid_= struct.unpack('i', _data[offset:offset+4])[0]; offset += 4;
|
||||
SPECTRUM.min_ = struct.unpack('f', _data[offset:offset+4])[0]; offset += 4;
|
||||
SPECTRUM.max_ = struct.unpack('f', _data[offset:offset+4])[0]; offset += 4;
|
||||
SPECTRUM.type_size_ = struct.unpack('i', _data[offset:offset+4])[0]; offset += 4;
|
||||
SPECTRUM.size_ = struct.unpack('i', _data[offset:offset+4])[0]; offset += 4;
|
||||
|
||||
if(SPECTRUM.type_size_ == 1): # unsigned 8 bit char
|
||||
SPECTRUM.values_ = numpy.fromstring( _data[offset:], dtype=numpy.ubyte ) # unsigned char
|
||||
elif(SPECTRUM.type_size_ == 2): # uint16_t
|
||||
SPECTRUM.values_ = numpy.fromstring( _data[offset:], dtype=numpy.ushort ) # uint16_t
|
||||
elif(SPECTRUM.type_size_ == 4): # 32float
|
||||
SPECTRUM.values_ = numpy.fromstring( _data[offset:], dtype=numpy.float32 )
|
||||
|
||||
|
||||
def HandleResponse_Demod(_data):
|
||||
global DEMOD
|
||||
offset = 0
|
||||
DEMOD.header_size_ = struct.unpack('i', _data[offset:offset+4])[0]; offset += 4;
|
||||
DEMOD.min_ = struct.unpack('f', _data[offset:offset+4])[0]; offset += 4;
|
||||
DEMOD.max_ = struct.unpack('f', _data[offset:offset+4])[0]; offset += 4;
|
||||
DEMOD.type_size_ = struct.unpack('i', _data[offset:offset+4])[0]; offset += 4;
|
||||
DEMOD.size_ = struct.unpack('i', _data[offset:offset+4])[0]; offset += 4;
|
||||
|
||||
if(DEMOD.type_size_ == 1): # unsigned 8 bit char
|
||||
DEMOD.values_ = numpy.fromstring( _data[offset:], dtype=numpy.ubyte ) # unsigned char
|
||||
elif(DEMOD.type_size_ == 2): # uint16_t
|
||||
DEMOD.values_ = numpy.fromstring( _data[offset:], dtype=numpy.ushort ) # uint16_t
|
||||
elif(DEMOD.type_size_ == 4): # 32float
|
||||
DEMOD.values_ = numpy.fromstring( _data[offset:], dtype=numpy.float32 )
|
||||
|
||||
|
||||
def UpdateState():
|
||||
SendCommand('get:frequency')
|
||||
SendCommand('get:sampling_rate')
|
||||
SendCommand('get:gain')
|
||||
SendCommand('get:baud')
|
||||
SendCommand('get:rtty_bits')
|
||||
SendCommand('get:rtty_stops')
|
||||
SendCommand('get:lowpass_bw')
|
||||
SendCommand('get:lowpass_trans')
|
||||
SendCommand('get:biastee')
|
||||
SendCommand('get:decimation')
|
||||
SendCommand('get:afc')
|
||||
|
||||
|
||||
def DecodeValues(i_data):
|
||||
'''
|
||||
convert values from 8/16/32 bits to 32 float
|
||||
'''
|
||||
true_values = [None] * len(i_data.values_)
|
||||
for i in xrange(len(i_data.values_)):
|
||||
value_encoded = i_data.values_[i]
|
||||
true_value = 0
|
||||
if(i_data.type_size_ == 1): # 8 bit char
|
||||
true_value = i_data.min_ + (float(value_encoded) / 255) * (i_data.max_ - i_data.min_)
|
||||
elif(i_data.type_size_ == 2): # uint16_t
|
||||
true_value = i_data.min_ + (float(value_encoded) / 65535) * (i_data.max_ - i_data.min_)
|
||||
elif(i_data.type_size_ == 4): # 32float
|
||||
true_value = value_encoded
|
||||
true_values[i] = true_value
|
||||
return true_values
|
|
@ -0,0 +1,354 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import sys
|
||||
import string
|
||||
import traceback
|
||||
from functools import partial
|
||||
try:
|
||||
import Tkinter as tk
|
||||
except:
|
||||
import tkinter as tk
|
||||
|
||||
import habdecClient as hdc
|
||||
|
||||
# rolling avg classs
|
||||
class Average:
|
||||
def __init__(self,max_count, init_val=0):
|
||||
self.max_count_ = max_count
|
||||
self.count_ = 0
|
||||
self.sum_ = 0
|
||||
self.add(init_val)
|
||||
|
||||
def get_avg(self):
|
||||
if self.count_ == 0:
|
||||
return self.sum_
|
||||
return float(self.sum_)/self.count_
|
||||
|
||||
def add(self, val):
|
||||
if self.count_ == self.max_count_:
|
||||
self.sum_ = self.get_avg() * (self.max_count_-1) + val
|
||||
else:
|
||||
self.count_ += 1
|
||||
self.sum_ += val
|
||||
|
||||
def reset(self, val):
|
||||
self.sum_ = val
|
||||
self.count_ = 1
|
||||
|
||||
|
||||
# GLOBAL VARIABLES:
|
||||
#
|
||||
WS = None # websocket
|
||||
|
||||
AccSpectrumArray = { # accumulated power spectrum values - used for smoothing out spectrum display
|
||||
'arr_': [],
|
||||
'count_': 0,
|
||||
'maxCount_': 3 # bigger values --> smoother but slower display
|
||||
}
|
||||
AvgCnt = 10 # Rolling average length. Smooths out spectrum display
|
||||
NoiseFloorAvg = Average(AvgCnt)
|
||||
SpectrumMinAvg = Average(AvgCnt)
|
||||
SpectrumMaxAvg = Average(AvgCnt)
|
||||
|
||||
|
||||
# gui
|
||||
GUI = tk.Tk() # tkinter
|
||||
SPECTRUM_CANVAS = None
|
||||
SPECTRUM_CANVAS_RES_X = 512
|
||||
SPECTRUM_CANVAS_RES_Y = 128
|
||||
SPECTRUM_CANVAS_BG = '#002244'
|
||||
ZOOM = 0
|
||||
DEMOD_CANVAS = None
|
||||
DEMOD_CANVAS_RES_X = 512
|
||||
DEMOD_CANVAS_RES_Y = 64
|
||||
DEMOD_CANVAS_BG = '#002244'
|
||||
CONTROLS = {}
|
||||
RTTY_STREAM_WIDGET = None
|
||||
RTTY_LAST_SENTENCE = None
|
||||
|
||||
|
||||
def AccumulateSpectrumArray(i_arr):
|
||||
if len(AccSpectrumArray['arr_']) != len(i_arr):
|
||||
AccSpectrumArray['arr_'] = i_arr
|
||||
AccSpectrumArray['count_'] = 1
|
||||
return AccSpectrumArray['arr_']
|
||||
|
||||
if AccSpectrumArray['count_'] < AccSpectrumArray['maxCount_']:
|
||||
for i in xrange(len(AccSpectrumArray['arr_'])):
|
||||
AccSpectrumArray['arr_'][i] += i_arr[i]
|
||||
AccSpectrumArray['count_'] += 1
|
||||
else:
|
||||
for i in xrange(len(AccSpectrumArray['arr_'])):
|
||||
AccSpectrumArray['arr_'][i] = (AccSpectrumArray['arr_'][i] / AccSpectrumArray['count_']) * (AccSpectrumArray['maxCount_']-1) + i_arr[i]
|
||||
|
||||
# return value
|
||||
spectr_result = [None] * len(AccSpectrumArray['arr_'])
|
||||
for i in xrange(len(AccSpectrumArray['arr_'])):
|
||||
spectr_result[i] = AccSpectrumArray['arr_'][i] / AccSpectrumArray['count_']
|
||||
return spectr_result
|
||||
|
||||
|
||||
def RedrawSpectrum():
|
||||
try:
|
||||
hdc.SendCommand( "power:res=" + str(SPECTRUM_CANVAS_RES_X) + ",zoom=" + str(ZOOM) )
|
||||
|
||||
if not len(hdc.SPECTRUM.values_):
|
||||
GUI.after(500, RedrawSpectrum)
|
||||
return
|
||||
|
||||
global SPECTRUM_CANVAS
|
||||
|
||||
SPECTRUM_CANVAS.delete('all')
|
||||
|
||||
zoom = ZOOM # [0-1) values
|
||||
|
||||
# LOWPASS FILTER DRAW
|
||||
#
|
||||
_lowpass_bw = hdc.STATE['lowpass_bw'] / (1.0 - .999*zoom)
|
||||
_lowpass_trans = hdc.STATE['lowpass_trans'] / (1.0 - .999*zoom)
|
||||
_l = max(0, .5 - .5 * _lowpass_bw)
|
||||
_r = min(1, .5 + .5 * _lowpass_bw)
|
||||
SPECTRUM_CANVAS.create_rectangle(
|
||||
_l*SPECTRUM_CANVAS_RES_X, 0,
|
||||
_r*SPECTRUM_CANVAS_RES_X, SPECTRUM_CANVAS_RES_Y-1,
|
||||
fill='#3333aa' )
|
||||
|
||||
# POWER SPECTRUM
|
||||
#
|
||||
spectrum_values_ = hdc.DecodeValues(hdc.SPECTRUM)
|
||||
|
||||
# resize vector to SPECTRUM_CANVAS_RES_X
|
||||
spectrum_values_resX = [None] * SPECTRUM_CANVAS_RES_X
|
||||
for x in xrange(SPECTRUM_CANVAS_RES_X):
|
||||
x_0_1 = float(x)/(SPECTRUM_CANVAS_RES_X-1)
|
||||
spectrum_values_resX[x] = spectrum_values_[ int( round(x_0_1 * (len(spectrum_values_)-1)) ) ]
|
||||
|
||||
# border values are NaN. needs fixing
|
||||
#spectrum_values_resX[0] = spectrum_values_resX[1]
|
||||
#spectrum_values_resX[-1] = spectrum_values_resX[-2]
|
||||
|
||||
# accumulate spectrum and noisefloor for nice rendering
|
||||
spectrum_values_resX = AccumulateSpectrumArray(spectrum_values_resX)
|
||||
NoiseFloorAvg.add(hdc.SPECTRUM.noise_floor_)
|
||||
SpectrumMinAvg.add(hdc.SPECTRUM.min_)
|
||||
SpectrumMaxAvg.add(hdc.SPECTRUM.max_)
|
||||
|
||||
noise_floor_avg = NoiseFloorAvg.get_avg()
|
||||
spectrum_min_avg = SpectrumMinAvg.get_avg()
|
||||
spectrum_max_avg = SpectrumMaxAvg.get_avg()
|
||||
|
||||
# draw
|
||||
for x in xrange(SPECTRUM_CANVAS_RES_X):
|
||||
val_0_1 = 1.0 - abs(spectrum_values_resX[x] / spectrum_min_avg)
|
||||
val_0_1 = max(val_0_1, 0)
|
||||
val_pixel = (1.0-val_0_1) * (SPECTRUM_CANVAS_RES_Y-1)
|
||||
val_pixel = int(round(val_pixel))
|
||||
SPECTRUM_CANVAS.create_line(x, SPECTRUM_CANVAS_RES_Y-1, x, val_pixel, fill='#CC0000')
|
||||
|
||||
# CENTER LINE
|
||||
#
|
||||
SPECTRUM_CANVAS.create_line(SPECTRUM_CANVAS_RES_X/2, SPECTRUM_CANVAS_RES_Y-1, SPECTRUM_CANVAS_RES_X/2, 0, fill='#888888')
|
||||
|
||||
# NOISE FLOOR
|
||||
#
|
||||
nf_0_1 = 1.0 - abs(noise_floor_avg / spectrum_min_avg)
|
||||
nf_0_1 = max(nf_0_1, 0)
|
||||
SPECTRUM_CANVAS.create_line( 0, (1.0-nf_0_1)*SPECTRUM_CANVAS_RES_Y - 1,
|
||||
SPECTRUM_CANVAS_RES_X-1, (1.0-nf_0_1)*SPECTRUM_CANVAS_RES_Y - 1,
|
||||
fill='#111111' )
|
||||
|
||||
# PEAK LEFT
|
||||
#
|
||||
colour = '#FF2200'
|
||||
if(hdc.SPECTRUM.peak_left_valid_):
|
||||
colour = '#FF2200'
|
||||
else:
|
||||
colour = '#113322'
|
||||
peak_left_0_1 = float(hdc.SPECTRUM.peak_left_) / hdc.SPECTRUM.size_
|
||||
SPECTRUM_CANVAS.create_line( peak_left_0_1 * SPECTRUM_CANVAS_RES_X, 0,
|
||||
peak_left_0_1 * SPECTRUM_CANVAS_RES_X, SPECTRUM_CANVAS_RES_Y - 1,
|
||||
fill=colour )
|
||||
|
||||
# PEAK RIGHT
|
||||
#
|
||||
if(hdc.SPECTRUM.peak_right_valid_):
|
||||
colour = '#0088FF'
|
||||
else:
|
||||
colour = '#113322'
|
||||
peak_right_0_1 = float(hdc.SPECTRUM.peak_right_) / hdc.SPECTRUM.size_
|
||||
SPECTRUM_CANVAS.create_line( peak_right_0_1 * SPECTRUM_CANVAS_RES_X, 0,
|
||||
peak_right_0_1 * SPECTRUM_CANVAS_RES_X, SPECTRUM_CANVAS_RES_Y - 1,
|
||||
fill=colour )
|
||||
except:
|
||||
print traceback.format_exc()
|
||||
|
||||
GUI.after(50, RedrawSpectrum)
|
||||
|
||||
|
||||
def RedrawDemod():
|
||||
try:
|
||||
hdc.SendCommand( "demod:res=" + str(DEMOD_CANVAS_RES_X) )
|
||||
|
||||
if not len(hdc.DEMOD.values_):
|
||||
GUI.after(500, RedrawDemod)
|
||||
return
|
||||
|
||||
global DEMOD_CANVAS
|
||||
|
||||
DEMOD_CANVAS.delete('all')
|
||||
|
||||
demod_values_ = hdc.DecodeValues(hdc.DEMOD)
|
||||
|
||||
# resize vector to SPECTRUM_CANVAS_RES_X
|
||||
demod_values_resX = [None] * DEMOD_CANVAS_RES_X
|
||||
for x in xrange(DEMOD_CANVAS_RES_X):
|
||||
x_0_1 = float(x)/(DEMOD_CANVAS_RES_X-1)
|
||||
demod_values_resX[x] = demod_values_[ int( round(x_0_1 * (len(demod_values_)-1)) ) ]
|
||||
|
||||
# draw
|
||||
prev_x = 0
|
||||
prev_y = 0
|
||||
for x in xrange(DEMOD_CANVAS_RES_X):
|
||||
val_ = demod_values_resX[x]
|
||||
val_ *= .75 # scale down a little bit
|
||||
val_ = .5 + .5 * val_ / max( abs( hdc.DEMOD.min_ ), abs( hdc.DEMOD.max_ ) )
|
||||
val_pixel = (1.0-val_) * (DEMOD_CANVAS_RES_Y-1)
|
||||
val_pixel = int(round(val_pixel))
|
||||
DEMOD_CANVAS.create_line(prev_x, prev_y, x, val_pixel, fill='#CC0000')
|
||||
prev_x = x
|
||||
prev_y = val_pixel
|
||||
except:
|
||||
print traceback.format_exc()
|
||||
|
||||
GUI.after(500, RedrawDemod)
|
||||
|
||||
|
||||
def RefreshRttyStream():
|
||||
hdc.SendCommand('liveprint')
|
||||
GUI.after( 250, RefreshRttyStream )
|
||||
|
||||
|
||||
def UpdateRtty():
|
||||
if RTTY_STREAM_WIDGET:
|
||||
RTTY_STREAM_WIDGET.delete('1.0', 'end')
|
||||
new_text = string.replace(hdc.RTTY_STREAM, '\n', ' ')
|
||||
if len(new_text) > 80:
|
||||
new_text = new_text[-80:]
|
||||
RTTY_STREAM_WIDGET.insert('end', new_text)
|
||||
|
||||
if RTTY_LAST_SENTENCE and len(hdc.SENTENCES):
|
||||
RTTY_LAST_SENTENCE.delete('1.0', 'end')
|
||||
RTTY_LAST_SENTENCE.insert('end', hdc.SENTENCES[-1])
|
||||
|
||||
GUI.after( 200, UpdateRtty )
|
||||
|
||||
|
||||
def ControlCallback(param_name, tk_var, i_type, *args):
|
||||
value = tk_var.get()
|
||||
if i_type == 'Bool': _command = 'set:%s=%s' % ( param_name, int(value) )
|
||||
else: _command = 'set:%s=%s' % ( param_name, str(value) )
|
||||
hdc.SendCommand(_command)
|
||||
|
||||
|
||||
def UpdateControls():
|
||||
#sync GUI with server state
|
||||
for param in CONTROLS.keys():
|
||||
CONTROLS[param][1].set( hdc.STATE[param] )
|
||||
|
||||
|
||||
def BuildMainWindow():
|
||||
grid_row = 0
|
||||
|
||||
# ZOOM BUTTON
|
||||
#
|
||||
def zoom(i_zoom):
|
||||
global ZOOM
|
||||
ZOOM += i_zoom
|
||||
ZOOM = max(0, min(ZOOM, 1))
|
||||
tk.Button(GUI, text = '-', command = lambda: zoom(-.1), width = 30).grid(row=grid_row, column=0)
|
||||
tk.Button(GUI, text = '+', command = lambda: zoom(.1), width = 30).grid(row=grid_row, column=1)
|
||||
grid_row += 1
|
||||
|
||||
# SPECTRUM CANVAS
|
||||
#
|
||||
global SPECTRUM_CANVAS
|
||||
SPECTRUM_CANVAS = tk.Canvas(GUI, width=SPECTRUM_CANVAS_RES_X, height=SPECTRUM_CANVAS_RES_Y, bg=SPECTRUM_CANVAS_BG)
|
||||
SPECTRUM_CANVAS.grid(row = grid_row, column=1)
|
||||
grid_row += 1
|
||||
|
||||
# DEMOD CANVAS
|
||||
#
|
||||
global DEMOD_CANVAS
|
||||
DEMOD_CANVAS = tk.Canvas(GUI, width=DEMOD_CANVAS_RES_X, height=DEMOD_CANVAS_RES_Y, bg=DEMOD_CANVAS_BG)
|
||||
DEMOD_CANVAS.grid(row = grid_row, column=1)
|
||||
grid_row += 1
|
||||
|
||||
# CONTROL WIDGETS
|
||||
#
|
||||
def MakeWidget(i_type, i_name, default_value, callback):
|
||||
var = None
|
||||
widget = None
|
||||
if i_type == 'Int':
|
||||
var = tk.IntVar(GUI, value=int(default_value))
|
||||
widget = tk.Entry( GUI, textvariable = var )
|
||||
widget.bind( '<Return>', lambda _var=var: callback(var, i_type) )
|
||||
elif i_type == 'Float':
|
||||
var = tk.DoubleVar(GUI, value=float(default_value))
|
||||
widget = tk.Entry( GUI, textvariable = var )
|
||||
widget.bind( '<Return>', lambda _var=var: callback(var, i_type) )
|
||||
elif i_type == 'Bool':
|
||||
var = tk.BooleanVar(GUI, value=bool(default_value))
|
||||
widget = tk.Checkbutton( GUI, variable = var, command = lambda _var=var: callback(var, i_type) )
|
||||
# var.trace( 'w', lambda name, index, mode, _var=var: callback(var, i_type) )
|
||||
|
||||
return [widget, var, i_type]
|
||||
|
||||
hdc.UpdateState()
|
||||
print(hdc.STATE)
|
||||
global CONTROLS
|
||||
CONTROLS['frequency'] = MakeWidget('Float', 'frequency', hdc.STATE['frequency'], partial(ControlCallback, 'frequency') )
|
||||
CONTROLS['gain'] = MakeWidget('Int', 'gain', hdc.STATE['gain'], partial(ControlCallback, 'gain') )
|
||||
CONTROLS['decimation'] = MakeWidget('Int', 'decimation', hdc.STATE['decimation'], partial(ControlCallback, 'decimation') )
|
||||
CONTROLS['baud'] = MakeWidget('Float', 'baud', hdc.STATE['baud'], partial(ControlCallback, 'baud') )
|
||||
CONTROLS['rtty_bits'] = MakeWidget('Int', 'rtty_bits', hdc.STATE['rtty_bits'], partial(ControlCallback, 'rtty_bits') )
|
||||
CONTROLS['rtty_stops'] = MakeWidget('Int', 'rtty_stops', hdc.STATE['rtty_stops'], partial(ControlCallback, 'rtty_stops') )
|
||||
CONTROLS['lowpass_bw'] = MakeWidget('Float', 'lowpass_bw', hdc.STATE['lowpass_bw'], partial(ControlCallback, 'lowpass_bw') )
|
||||
CONTROLS['lowpass_trans'] = MakeWidget('Float', 'lowpass_trans',hdc.STATE['lowpass_trans'], partial(ControlCallback, 'lowpass_trans') )
|
||||
CONTROLS['biastee'] = MakeWidget('Bool', 'biastee', hdc.STATE['biastee'], partial(ControlCallback, 'biastee') )
|
||||
CONTROLS['afc'] = MakeWidget('Bool', 'afc', hdc.STATE['afc'], partial(ControlCallback, 'afc') )
|
||||
|
||||
for param in [ 'frequency', 'gain', 'decimation', 'lowpass_bw', 'lowpass_trans', 'baud', 'rtty_bits', 'rtty_stops', 'biastee', 'afc']:
|
||||
widget = CONTROLS[param]
|
||||
tk.Label(text=param).grid(row=grid_row, column=0)
|
||||
widget[0].grid(row=grid_row, column=1)
|
||||
grid_row += 1
|
||||
|
||||
UpdateControls()
|
||||
|
||||
# RTTY STREAM
|
||||
#
|
||||
tk.Label(text='RTTY Stream').grid(row=grid_row, column=0)
|
||||
global RTTY_STREAM_WIDGET
|
||||
RTTY_STREAM_WIDGET = tk.Text(GUI, width=80, height=1)
|
||||
RTTY_STREAM_WIDGET.grid(row=grid_row, column=1)
|
||||
grid_row += 1
|
||||
|
||||
# LAST SENTENCE
|
||||
#
|
||||
tk.Label(text='Last Sentence').grid(row=grid_row, column=0)
|
||||
global RTTY_LAST_SENTENCE
|
||||
RTTY_LAST_SENTENCE = tk.Text(GUI, width=80, height=1)
|
||||
RTTY_LAST_SENTENCE.grid(row=grid_row, column=1)
|
||||
grid_row += 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
hdc.InitConnection("ws://localhost:5555/", UpdateControls, UpdateRtty)
|
||||
hdc.UpdateState()
|
||||
|
||||
BuildMainWindow()
|
||||
GUI.after( 500, RedrawSpectrum )
|
||||
GUI.after( 500, RedrawDemod )
|
||||
GUI.after( 250, RefreshRttyStream )
|
||||
GUI.mainloop()
|
||||
hdc.CloseConnection()
|
Plik binarny nie jest wyświetlany.
Po Szerokość: | Wysokość: | Rozmiar: 32 KiB |
Ładowanie…
Reference in New Issue