I tried a little experiment, implementing some code in numpy (usually I build modules in c++ to interface to python). Since these operations are all large vectors, I hoped it would be reasonably efficient.
The code in question is simple. It is a model of an amplifier, modeled by it's AM/AM and AM/PM characteristics. The function in question is the __call__ operator. The test program plots a spectrum, calling this operator 1024 times each time with a vector of 4096. Any ideas? The code is not too big, so I'll try to attach it.
from linear_interp import linear_interp from numpy import * from math import pi from uvector import vector_Complex def db_to_pwr (db): return 10**(0.1*db) def db_to_volt (db): return 10**(0.05*db) def db_to_gain (db_in, db_out): return 10**(0.05*(db_out-db_in)) def from_gain_and_phase (gain, phase): out = empty (len (gain), dtype=complex) out.real = cos (phase) * gain out.imag = sin (phase) * gain return out class ampl (object): def __init__ (self, pin, pout, deg, max_pin, delta_v, ibo): "pin, pout in dB normalized to sat, ibo in dB" ampl_interp = linear_interp (vectorize (db_to_volt) (pin), db_to_volt (pout)) phase_interp = linear_interp (vectorize (db_to_volt)(pin), array(deg)*pi/180) ## These would be used if input was linear instead of db ## ampl_interp = linear_interp (sqrt (pin), sqrt (pout)) ## phase_interp = linear_interp (sqrt (pin), array(deg)*pi/180) eps = 1e-6 max_vin = sqrt (max_pin) vin = arange (0, max_vin+eps, delta_v) gain = vectorize (ampl_interp) (vin)/vin ##gain = vectorize (ampl_interp) (vin**2) / vin #gain[0] = 1 gain[0] = gain[1] # avoid singularity phase = vectorize (phase_interp) (vin**2) self.cmplx_gain = from_gain_and_phase (gain, phase) self.delta_v = delta_v self.ibo = 10**(-0.05*ibo) self._phase_comp = from_gain_and_phase (ones (len (phase)), -phase) def __call__ (self, zin): zin_ibo = zin * self.ibo vin = abs (array (zin_ibo, dtype=complex)) index = vin / self.delta_v lower = floor (index).astype (int) upper = lower + 1 assert (alltrue (upper < len (self.cmplx_gain))) delta = (index - lower) cmplx_gain = self.cmplx_gain[lower]*(1-delta) + self.cmplx_gain[upper]*delta return vector_Complex (cmplx_gain * zin_ibo) def phase_comp (self, zin): zin_ibo = zin * self.ibo vin = abs (array (zin_ibo, dtype=complex)) index = vin / self.delta_v lower = floor (index).astype (int) upper = lower + 1 assert (alltrue (upper < len (self._phase_comp))) delta = (index - lower) return vector_Complex (self._phase_comp[lower]*(1-delta) + self._phase_comp[upper]*delta) if __name__ == "__main__": x = array ((-20, -10, 0, 10), dtype=float) y = array ((-20, -10, 0, 0), dtype=float) p = array ((-20, -10, 0, 0), dtype=float) t = ampl (x, y, p, 10, .01, 0) def pwr_to_cmplx (db): return complex (10**(0.05*db)) db = (-20, -10, 0, 1) res = array (t (vectorize (pwr_to_cmplx) (db)), dtype=complex) #print res def mag_sqr (z): return z.real*z.real + z.imag*z.imag db_out = log10 (vectorize(mag_sqr) (res)) * 10 phase_out = arctan2 (res.imag, res.real)*180/pi print db_out, phase_out
class linear_interp (object): def __init__ (self, x, y): assert (len (x) == len (y)) self.n = len (x) self.x = tuple (x) self.y = tuple (y) def __call__ (self, _x): klo = 1 khi = self.n while (khi - klo > 1): k = (khi + klo) >> 1 if (self.x[k - 1] > _x): khi = k else: klo = k h = float (self.x[khi - 1]-self.x[klo - 1]) if (h == 0.0): raise ValueError, "Bad x input to routine linear_interp" a = (self.x[khi - 1]-_x) / h b = (_x - self.x[klo - 1]) / h return a * self.y[klo - 1] + b * self.y[khi - 1] if (__name__ == "__main__"): x = (xrange (10)) y = [e+1 for e in x] l = linear_interp (x, y) print l (0) print l (1) print l (0.5)
import sys sys.path.append ('../mod') from constellation import * from boost_rand import rng, pn, normal_c, normal, uniform_real, uniform_int, gold_code_generator, beta from nyquist import * from fir import coef_from_func_double, FIR_Complex_double, InterpFIR_Complex_double, DecimFIR_Complex_double from ampl import ampl #from polar_ampl import ampl from fft3 import * from uvector import vector_double, vector_Complex, mag_sqr, log10 from stats import stat_Complex from limit import Limit import math from optparse import OptionParser parser = OptionParser() parser.add_option ("--sps", type=int, default=4) parser.add_option ("-n", "--pts", type=int, default=1024) parser.add_option ("-m", "--sets", type=int, default=1024) parser.add_option ("-a", "--alpha", type=float, default=0.25) parser.add_option ("--si", type=int, default=16) parser.add_option ("--ibo", type=float, default=0) parser.add_option ("--linear", action="store_true") (opt, args) = parser.parse_args() scale = 1 xmit_coef = coef_from_func_double (RNyquistPulse (opt.alpha, scale), 1./opt.sps, opt.sps*opt.si) mf_coef = coef_from_func_double (RNyquistPulse (opt.alpha, scale), 1./opt.sps, opt.sps*opt.si)/opt.sps rcv_filt = DecimFIR_Complex_double (mf_coef, opt.sps, 0) CONSTELLATION_POINTS = ( complex ( 0.9239, 0.3827 ), complex ( 0.9239, -0.3827 ), complex ( -0.9239, 0.3827 ), complex ( -0.9239, -0.3827 ), complex ( 0.3827, 0.9239 ), complex ( 0.3827, -0.9239 ), complex ( -0.3827, 0.9239 ), complex ( -0.3827, -0.9239 ) ) const = QAMconstellation (CONSTELLATION_POINTS) rng1 = rng() pn3 = pn (rng1, 3) the_fft = fft (opt.pts, FORWARD) xmit_filt = InterpFIR_Complex_double (xmit_coef, opt.sps) import numpy as np Id= np.array ([50.0*10**(0.05*-100), 50.0, 100.0, 150.0, 200.0, 250.0, 300.0, 350.0, 400.0, 450.0, 500.0, 550.0, 600.0, 650.0, 700.0, 750.0, 800.0, 850.0, 900.0, 950.0, 1000.0, 1050.0]) pout= np.array ([-100, 0.0, 7.4000000000000004, 11.4, 14.199999999999999, 16.300000000000001, 18.100000000000001, 19.300000000000001, 20.600000000000001, 21.600000000000001, 22.600000000000001, 23.399999999999999, 24.100000000000001, 24.899999999999999, 25.399999999999999, 26.100000000000001, 26.5, 26.899999999999999, 27.100000000000001, 27.300000000000001, 27.399999999999999, 27.399999999999999]) phase= np.array ([105.0, 105.0, 101.0, 97.0, 94.0, 91.0, 88.0, 85.0, 81.0, 79.0, 76.0, 74.0, 73.0, 70.0, 68.0, 63.0, 58.0, 52.0, 47.0, 39.0, 31.0, 23.0]) #phase= np.array ([105.0, 101.0, 97.0, 94.0, 91.0, 88.0, 85.0, 81.0, 79.0, 76.0, 74.0, 73.0, 70.0, 68.0, 63.0, 58.0, 52.0, 47.0, 39.0, 31.0, 23.0]) phase = np.zeros (len (pout)) #Normalize to saturation point pout -= np.amax (pout) Id /= Id[np.argmax (pout)] phase -= phase[0] pin = 20*np.log10(Id) xaxis = np.array (xrange (the_fft.size),dtype=float)*opt.sps/the_fft.size #print xaxis for opt.ibo in 0,1,2,3: the_sum = vector_double (opt.pts, 0) pwr_stat = stat_Complex() rcv_pwr_stat = stat_Complex() #for c++ ampl #amplifier = ampl (vector_double (pin), vector_double (pout), vector_double (phase), pin[-1]+10, 0.01, opt.ibo) #for numpy python ampl amplifier = ampl (pin, pout, phase, pin[-1]+10, 0.01, opt.ibo) for block in xrange (opt.sets): symbols = opt.pts / opt.sps xsyms = pn3 (symbols) xconst = const (xsyms) mod_out = xmit_filt (xconst) if (not opt.linear): mod_out = amplifier (amplifier.phase_comp (mod_out) * mod_out) the_sum += mag_sqr (the_fft (mod_out)/the_fft.size) pwr_stat += mod_out rcv_pwr_stat += rcv_filt (mod_out) the_sum /= opt.sets print >> sys.stderr, 'ibo:', opt.ibo, 'obo:', math.log10 (pwr_stat.Var())*10, 'rcv_pwr:', math.log10 (rcv_pwr_stat.Var()) * 10 from matplotlib import pylab pylab.grid (True) pylab.plot (xaxis, np.array (log10 (the_sum)*10)) the_sum = vector_double (opt.pts, 0) pwr_stat = stat_Complex() rcv_pwr_stat = stat_Complex() for block in xrange (opt.sets): symbols = opt.pts / opt.sps xsyms = pn3 (symbols) xconst = const (xsyms) mod_out = xmit_filt (xconst) if (not opt.linear): mod_out = Limit (mod_out) the_sum += mag_sqr (the_fft (mod_out)/the_fft.size) pwr_stat += mod_out rcv_pwr_stat += rcv_filt (mod_out) the_sum /= opt.sets print >> sys.stderr, 'obo:', math.log10 (pwr_stat.Var())*10, 'rcv_pwr:', math.log10 (rcv_pwr_stat.Var()) * 10 from matplotlib import pylab pylab.grid (True) pylab.plot (xaxis, np.array (log10 (the_sum)*10)) pylab.show()
_______________________________________________ Numpy-discussion mailing list Numpy-discussion@scipy.org http://projects.scipy.org/mailman/listinfo/numpy-discussion