tinypythonpanadapter/iq_wf.py

131 wiersze
4.9 KiB
Python
Executable File

#!/usr/bin/env python
# Program iq_wf.py - Create waterfall spectrum display.
# Copyright (C) 2013-2014 Martin Ewing
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Contact the author by e-mail: aa6e@arrl.net
#
# Part of the iq.py program.
# HISTORY
# 01-04-2014 Initial release
import pygame as pg
import numpy as np
import math, sys
def palette_color(palette, val, vmin0, vmax0):
""" translate a data value into a color according to several different
methods. (PALETTE variable)
input: value of data, minimum value, maximum value for transform
return: pygame color tuple
"""
f = (float(val) - vmin0) / (vmax0 - vmin0) # btw 0 and 1.0
f *= 2
f = min(1., max(0., f))
if palette == 1:
g, b = 0, 0
if f < 0.333:
r = int(f*255*3)
elif f < 0.666:
r = 200
g = int((f-.333)*255*3)
else:
r = 200
g = 200
b = int((f-.666)*255*3)
elif palette == 2:
bright = min (1.0, f + 0.15)
tpi = 2 * math.pi
r = bright * 128 *(1.0 + math.cos(tpi*f))
g = bright * 128 *(1.0 + math.cos(tpi*f + tpi/3))
b = bright * 128 *(1.0 + math.cos(tpi*f + 2*tpi/3))
else:
print "Invalid palette requested!"
sys.exit()
return ( max(0,min(255,r)), max(0,min(255,g)), max(0,min(255,b)) )
class Wf(object):
""" Make a waterfall '3d' display of spectral power vs frequency & time.
init: min, max palette parameter, no. of steps between min & max,
size for each freq,time data plot 'pixel' (a box)
"""
def __init__(self, opt, vmin, vmax, nsteps, pxsz):
""" Initialize data and
pre-calculate palette & filled rect surfaces, based on vmin, vmax,
no. of surfaces = nsteps
"""
self.opt = opt
self.vmin = vmin
self.vmin_rst = vmin
self.vmax = vmax
self.vmax_rst = vmax
self.nsteps = nsteps
self.pixel_size = pxsz
self.firstcalc = True
self.initialize_palette()
def initialize_palette(self):
""" Set up surfaces for each possible color value in list self.pixels.
"""
self.pixels = list()
for istep in range(self.nsteps):
ps = pg.Surface(self.pixel_size)
val = float(istep)*(self.vmax-self.vmin)/self.nsteps + self.vmin
color = palette_color(self.opt.waterfall_palette, val, self.vmin, self.vmax)
ps.fill( color )
self.pixels.append(ps)
def set_range(self, vmin, vmax):
""" define a new data range for palette calculation going forward.
input: vmin, vmax
"""
self.vmin = vmin
self.vmax = vmax
self.initialize_palette()
def reset_range(self):
""" reset palette data range to original settings.
"""
self.vmin = self.vmin_rst
self.vmax = self.vmax_rst
self.initialize_palette()
return self.vmin, self.vmax
def calculate(self, datalist, nsum, surface): # (datalist is np.array)
if self.firstcalc: # First time through,
self.datasize = len(datalist) # pick up dimension of datalist
self.wfacc = np.zeros(self.datasize) # and establish accumulator
self.dx = float(surface.get_width()) / self.datasize # x spacing of wf cells
# Note: self.dx must be >= 1
self.wfcount = 0
self.firstcalc = False
self.wfcount += 1
self.wfacc += datalist # Accumulate data
if self.wfcount % nsum != 0: # Don't plot wf data until enough spectra accumulated
return
else:
surface.blit(surface, (0, self.pixel_size[1])) # push old wf down one row
for ix in xrange(self.datasize):
v = datalist[ix] #self.wfacc[ix] / nsum #datalist[ix] # dB units
vi = int( self.nsteps * (v-self.vmin) / (self.vmax-self.vmin) )
vi = max(0, min(vi, self.nsteps-1) )
px_surf = self.pixels[vi]
x = int(ix * self.dx)
surface.blit(px_surf, (x, 0))
self.wfcount = 0 # Initialize counter
self.wfacc.fill(0) # and accumulator