Added example python client.

pull/6/head
Michal Fratczak 2019-01-08 01:22:41 +01:00
rodzic 4ac7295632
commit ab1386a5d0
4 zmienionych plików z 621 dodań i 0 usunięć

Wyświetl plik

@ -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.

Wyświetl plik

@ -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

Wyświetl plik

@ -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