Subversion Repositories pentevo

Rev

Rev 1243 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. #!/usr/bin/env python
  2.  
  3. import sys
  4. from sympy import *
  5. import math
  6. import cmath
  7. import random
  8.  
  9. #e24 = [1.0, 1.1, 1.2, 1.3, 1.5, 1.6, 1.8, 2.0, 2.2, 2.4, 2.7, 3.0, 3.3, 3.6, 3.9, 4.3, 4.7, 5.1, 5.6, 6.2, 6.8, 7.5, 8.2, 9.1];
  10. #e6  = [1.0,                1.5,                2.2,                3.3,                4.7,                6.8               ];
  11.  
  12.  
  13.  
  14. def calc_mfb_lowpass():
  15.  
  16.     # as in sloa049, fig.7-1
  17.     r1,r2,r3,c1,c2 = symbols('r1 r2 r3 c1 c2', real=True, positive=True)
  18.  
  19.     # v is input voltage, u is output, x is voltage at r1/c2/r2/r3 node
  20.     u,v,x = symbols('u v x')
  21.  
  22.     # currents through appropriate elements.
  23.     # ir1,ir2,ir3 flow into the node x,
  24.     # ic2 flows out of the node x
  25.     ir1,ic2,ir2,ir3 = symbols('ir1,ic2,ir2,ir3')
  26.  
  27.     # angular frequency (omega)
  28.     w = symbols('w', real=True, positive=True)
  29.  
  30.     # make set of equations to calculate H
  31.     #
  32.     # current through r1
  33.     h_eq1 = Eq( ir1, (v-x)/r1 )
  34.     #
  35.     # current through c2
  36.     h_eq2 = Eq( ic2, x/(1/(I*w*c2)) )
  37.     #
  38.     # current through r2
  39.     h_eq3 = Eq( ir2, (u-x)/r2 )
  40.     #
  41.     # current through r3/c1
  42.     h_eq4 = Eq( ir3, (u-x)/(r3 + 1/(I*w*c1)) )
  43.     #
  44.     # node x charge conservation
  45.     h_eq5 = Eq( ir1+ir2+ir3, ic2 )
  46.     #
  47.     # opamp NFB condition
  48.     h_eq6 = Eq( u-ir3*(1/(I*w*c1)), 0 )
  49.  
  50.     # solve the set
  51.     u_solve = solve( [h_eq1, h_eq2, h_eq3, h_eq4, h_eq5, h_eq6], [x,u,ir1,ic2,ir2,ir3], dict=True, domain=S.Complexes )
  52.  
  53.     if len(u_solve)!=1:
  54.         sys.stderr.write("Many or no solutions: {} !\n".format(u_solve))
  55.         exit(1)
  56.    
  57.     u_expr = u_solve[0]
  58.  
  59.     h_expr = u_expr[u]/v
  60.  
  61.     print(h_expr)
  62.  
  63.  
  64.  
  65.     # now make more substitutions
  66.     #
  67.     h = symbols('h')
  68.     k = symbols('k', real=True, negative=True)
  69.     q,wc = symbols('q wc', real=True, positive=True)
  70.     #
  71.     # filter gain
  72.     s_eq1 = Eq( k, -r2/r1 )
  73.     #
  74.     # cutoff angular frequency
  75.     s_eq2 = Eq( wc, 1/sqrt(r2*r3*c1*c2) )
  76.     #
  77.     # quality
  78.     s_eq3  = Eq( q, sqrt(r2*r3*c1*c2)/(r3*c1+r2*c1-r3*c1*k) )
  79.     #s_neq3 = Lt( k, 0 )
  80.     #
  81.     # new H expression
  82.     s_eq4 = Eq( h, h_expr )
  83.  
  84.     # solve
  85.     h_solve = solve( [s_eq1, s_eq2, s_eq3, s_eq4], [h, r1, r2, r3, c1, c2], dict=True )
  86.  
  87.     if len(h_solve)!=1:
  88.         sys.stderr.write("Many or no solutions: {} !\n".format(h_solve))
  89.         exit(1)
  90.  
  91.     h_expr = h_solve[0]
  92.     h_result = h_expr[h]
  93.  
  94.     init_printing()
  95.     print('')
  96.     #pprint(h_expr)
  97.     print('')
  98.     pprint(Eq(h,h_result))
  99.  
  100.  
  101.     rc_solve = solve( [s_eq1, s_eq2, s_eq3], [r3,c2], dict=True )
  102.  
  103.     rc_expr = rc_solve[0]
  104.  
  105.     print('')
  106.     pprint(Eq(c2,rc_expr[c2]))
  107.     print('')
  108.     pprint(Eq(r3,rc_expr[r3]))
  109.     print('')
  110.     pprint(s_eq1)
  111.     print('')
  112.     pprint(s_eq2)
  113.     print('')
  114.     pprint(s_eq3)
  115.  
  116. # these formulae calculated symbolically by the function above and are set here in comments
  117. # as reference for further numerical calculations.
  118. """
  119.         ____________
  120.       ╲╱ c₁⋅c₂⋅r₂⋅r₃
  121. q = ─────────────────────
  122.     c₁⋅((1 - k)⋅r₃ + r₂)
  123.  
  124.            1            
  125. wc = ───────────────
  126.       ____________
  127.     ╲╱ c₁⋅c₂⋅r₂⋅r₃
  128.  
  129.  
  130.    -r₂
  131. k = ────
  132.     r₁
  133.  
  134.                  2        
  135.            k⋅q⋅wc        
  136. h = ───────────────────────
  137.         2       2        
  138.    - q⋅w  + q⋅wc  + ⅈ⋅w⋅wc
  139.  
  140.           q⋅(k - 1)      
  141. c₂ = ──────────────────────
  142.     r₂⋅wc⋅(c₁⋅q⋅r₂⋅wc - 1)
  143.  
  144.     c₁⋅q⋅r₂⋅wc - 1
  145. r₃ = ───────────────
  146.     c₁⋅q⋅wc⋅(k - 1)
  147.  
  148. """
  149.  
  150.  
  151. def select_pvalues_list(values='e24'):
  152. # returns [1.0, ..., 9.1] list (for e24)
  153.  
  154.     if values=='e24':
  155.         r = [1.0, 1.1, 1.2, 1.3, 1.5, 1.6, 1.8, 2.0, 2.2, 2.4, 2.7, 3.0, 3.3, 3.6, 3.9, 4.3, 4.7, 5.1, 5.6, 6.2, 6.8, 7.5, 8.2, 9.1]
  156.     elif values=='e12':
  157.         r = [1.0,      1.2,      1.5,      1.8,      2.2,      2.7,      3.3,      3.9,      4.7,      5.6,      6.8,      8.2     ]
  158.     elif values=='e6':
  159.         r = [1.0,                1.5,                2.2,                3.3,                4.7,                6.8               ]
  160.     else:
  161.         sys.stderr.write('wrong argument given!\n')
  162.         raise NameError('stop here')
  163.         sys.exit(1)
  164.  
  165.     r = [r[-1]*0.1] + r + [r[0]*10.0]
  166.  
  167.     return r
  168.  
  169.  
  170.  
  171.  
  172. def select_two_nearest(val,val_exp,pvalues_name='e24'):
  173.  
  174.     def my_lt(v1,v2):
  175.         return (v1<v2) and (not math.isclose(v1,v2,rel_tol=1e-3))
  176.  
  177.     def my_gt(v1,v2):
  178.         return (v1>v2) and (not math.isclose(v1,v2,rel_tol=1e-3))
  179.  
  180.  
  181.     pvalues = select_pvalues_list(pvalues_name)
  182.  
  183.     # select range
  184.     if my_lt(val, min(pvalues[1:-1])):
  185.         while my_lt(val, min(pvalues[1:-1])):
  186.             if my_lt(val, max(pvalues[1:-1])*0.1):
  187.                 val     = val     * 10.0;
  188.                 val_exp = val_exp *  0.1;
  189.             else:
  190.                 break;
  191.  
  192.     elif my_gt(val, max(pvalues[1:-1])):
  193.         while my_gt(val, max(pvalues[1:-1])):
  194.             if my_gt(val, min(pvalues[1:-1])*10.0):
  195.                 val     = val     *  0.1;
  196.                 val_exp = val_exp * 10.0;
  197.             else:
  198.                 break;
  199.  
  200.  
  201.     # select nearest values
  202.     for i in range(len(pvalues)-1):
  203.         vdn = pvalues[i];
  204.         vup = pvalues[i+1];
  205.         if vdn<=val and val<=vup:
  206.             break;
  207.  
  208.     vdn_exp = val_exp
  209.     vup_exp = val_exp
  210.  
  211.     if vdn<pvalues[1]:
  212.         vdn *= 10.0
  213.         vdn_exp *= 0.1
  214.  
  215.     if vup>pvalues[-2]:
  216.         vup *= 0.1
  217.         vup_exp *= 10.0
  218.  
  219.     return [ (vdn,vdn_exp), (vup,vup_exp) ];
  220.  
  221. def select_one_nearest(val,pvalues_name='e24'):
  222.  
  223.     lval = select_two_nearest(val,1.0,pvalues_name)
  224.  
  225.     (vdn,vdn_exp) = lval[0]
  226.     (vup,vup_exp) = lval[1]
  227.  
  228.     vdn *= vdn_exp
  229.     vup *= vup_exp
  230.  
  231.     if math.fabs( (vdn-val)/val ) <= math.fabs( (vup-val)/val ):
  232.         return vdn
  233.     else:
  234.         return vup
  235.  
  236. def get_neighbour_value(val,dir='up',pvalues_name='e24'):
  237.    
  238.     pvalues = select_pvalues_list(pvalues_name)
  239.     exp = 1.0
  240.  
  241.     found_v = None
  242.     found_idx = None
  243.     for idx,v in enumerate(pvalues[1:-1]):
  244.         if math.isclose(val,v,rel_tol=1e-3):
  245.             found_v = v
  246.             found_idx = idx
  247.  
  248.             #print(' found {} {}'.format(found_v,found_idx))
  249.             break
  250.    
  251.     if found_v is None:
  252.         raise NameError('found_v is None!')
  253.         sys.exit(1)
  254.  
  255.     if dir=='up':
  256.         if math.isclose(found_v,pvalues[-2],rel_tol=1e-3):
  257.             next_v = pvalues[1]
  258.             exp = 10.0
  259.         else:
  260.             tmp = pvalues[1:-1]
  261.             #print('  tmp {}'.format(tmp))
  262.             next_v = tmp[found_idx+1]
  263.        
  264.     elif dir=='down':
  265.         if math.isclose(found_v,pvalues[1],rel_tol=1e-3):
  266.             next_v = pvalues[-2]
  267.             exp = 0.1
  268.         else:
  269.             tmp = pvalues[1:-1]
  270.             next_v = tmp[found_idx-1]
  271.  
  272.     else:
  273.         raise NameError('dir is neither "up" nor "down"!')
  274.         sys.exit(1)
  275.  
  276.     return (next_v,exp)
  277.  
  278.  
  279. def val_exp_le( val1, val1_exp, val2, val2_exp ):
  280.  
  281.     return (val1_exp < val2_exp) or ( math.isclose(val1_exp, val2_exp, rel_tol=1e-3) and ( (val1 < val2) or math.isclose(val1, val2, rel_tol=1e-3) ) )
  282.  
  283.  
  284. def select_values_in_range(vrange, vrange_exp, pvalues='e24'):
  285. # range is [min,max]
  286. # returns list of values already multiplied by exp
  287.  
  288.     vmin=vrange[0]
  289.     vmax=vrange[1]
  290.  
  291.     if vmax<=vmin:
  292.         raise NameError('wrong vrange: [0] must be bigger than [1]')
  293.         sys.exit(1)
  294.  
  295.     # expand interval by nearest preferred values
  296.     vmin_vals = select_two_nearest(vmin,vrange_exp, pvalues_name=pvalues)
  297.     vmax_vals = select_two_nearest(vmax,vrange_exp, pvalues_name=pvalues)
  298.     #print(vmin,vmax,vrange_exp)
  299.     #print(vmin_vals,vmax_vals)
  300.  
  301.  
  302.     # start building values list
  303.  
  304.     vmin_val_0, vmin_exp_0 = vmin_vals[0]
  305.     vmin_val_1, vmin_exp_1 = vmin_vals[1]
  306.     vmax_val_0, vmax_exp_0 = vmax_vals[0]
  307.     vmax_val_1, vmax_exp_1 = vmax_vals[1]
  308.  
  309.     if math.isclose(vmin_val_1*vmin_exp_1, vmin*vrange_exp, rel_tol=1e-3):
  310.         curr_v, curr_exp = vmin_vals[1]
  311.     else:
  312.         curr_v, curr_exp = vmin_vals[0]
  313.  
  314.     if math.isclose(vmax_val_0*vmax_exp_0, vmax*vrange_exp, rel_tol=1e-3):
  315.         vmax_v, vmax_exp = vmax_vals[0]
  316.     else:
  317.         vmax_v, vmax_exp = vmax_vals[1]
  318.  
  319.     values_range = []
  320.  
  321.     #print('vmaxs: {} {}'.format(vmax_v, vmax_exp))
  322.     while val_exp_le( curr_v, curr_exp, vmax_v, vmax_exp ):
  323.      
  324.         #print('currs: {} {}'.format(curr_v, curr_exp))
  325.  
  326.         values_range = values_range + [ (curr_v, curr_exp) ]
  327.  
  328.         next_v, exp_update = get_neighbour_value( curr_v, dir='up', pvalues_name=pvalues )
  329.        
  330.         #print('nexts: {} {}\n'.format(next_v, exp_update))
  331.  
  332.         curr_v = next_v
  333.         curr_exp *= exp_update
  334.  
  335.    
  336.     return values_range
  337.  
  338.  
  339.  
  340. def calc_mfb2_parameters(r1,r2,r3,c1,c2):
  341.  
  342.     wc = 1.0/math.sqrt(c1*c2*r2*r3)
  343.     k  = (-r2)/r1
  344.     q  = 1.0 / (wc * c1 * ((1.0-k)*r3 + r2))
  345.  
  346.     return (wc,k,q)
  347.  
  348.  
  349. def randomize_mfb2(r1,r2,r3,c1,c2, c_precision=0.05, r_precision=0.01, iters=1000):
  350.  
  351.     (wc0, k0, q0) = calc_mfb2_parameters(r1,r2,r3,c1,c2)
  352.  
  353.     d_wc = 0.0
  354.     d_k  = 0.0
  355.     d_q  = 0.0
  356.  
  357.     for i in range(iters):
  358.         dr1 = random.uniform(1.0-r_precision,1.0+r_precision)
  359.         dr2 = random.uniform(1.0-r_precision,1.0+r_precision)
  360.         dr3 = random.uniform(1.0-r_precision,1.0+r_precision)
  361.  
  362.         dc1 = random.uniform(1.0-c_precision,1.0+c_precision)
  363.         dc2 = random.uniform(1.0-c_precision,1.0+c_precision)
  364.  
  365.         rr1 = r1 * dr1
  366.         rr2 = r2 * dr2
  367.         rr3 = r3 * dr3
  368.  
  369.         rc1 = c1 * dc1
  370.         rc2 = c2 * dc2
  371.  
  372.         (wc,k,q) = calc_mfb2_parameters(rr1,rr2,rr3,rc1,rc2)
  373.  
  374.         d_wc = max(d_wc, abs(wc-wc0))
  375.         d_k  = max(d_k,  abs(k-k0)  )
  376.         d_q  = max(d_q,  abs(q-q0)  )
  377.  
  378.     return (d_wc/wc0, d_k/k0, d_q/q0)
  379.  
  380.  
  381.  
  382. def calc_filter( wc_set=2*math.pi*30000, q_set=1.0, k_set=(-1.17), r1_range=[20.0,30.0], r1_exp=1e3, r1_div=3.0, r1_add=0.0, r_pvalues='e24', c1_range=[33.0,4700.0], c1_exp=1e-12, c_pvalues='e12'):
  383.  
  384.     c1_values = select_values_in_range( c1_range, c1_exp, pvalues=c_pvalues )
  385.  
  386.     r1x3_values = select_values_in_range( [x for x in r1_range], r1_exp, pvalues=r_pvalues )
  387.     print(r1_range,r1_exp,r1x3_values)
  388.     r1_values = [(x/r1_div+r1_add/y,y) for (x,y) in r1x3_values]
  389.  
  390.     print( 'c1 values for sweep: {}'.format([x*y for (x,y) in c1_values]) )
  391.     print( 'r1 values for sweep: {}'.format([x*y for (x,y) in r1_values]) )
  392.  
  393.     print( '\ncalculating filter for: q={}, k={}, fc={}'.format(q_set,k_set,wc_set/(2*math.pi)) )
  394.  
  395.  
  396.     f_values=[]
  397.  
  398.     for r1 in [x*y for (x,y) in r1_values]:
  399.         for c1 in [x*y for (x,y) in c1_values]:
  400.  
  401.             r2_ideal = (-1) * k_set * r1
  402.  
  403.             c2_ideal = (q_set * (k_set - 1.0)) / (r2_ideal * wc_set * (c1 * q_set * r2_ideal * wc_set - 1.0))
  404.  
  405.            
  406.             r3_ideal = (c1 * q_set * r2_ideal * wc_set - 1.0) / ( (k_set - 1.0) * q_set * wc_set * c1 )
  407.  
  408.             if r2_ideal<=0.0 or c2_ideal<=0.0 or r3_ideal<=0.0:
  409.                 continue
  410.  
  411.             print('\ntry c1={:1.1e}, r1={:.1f}:'.format(c1,r1))
  412.             print(' ideal values: r2={:.1f}, r3={:.1f}, c2={:1.4e}'.format(r2_ideal, r3_ideal, c2_ideal))
  413.  
  414.             r2_real = select_one_nearest(r2_ideal, r_pvalues)
  415.             c2_real = select_one_nearest(c2_ideal, c_pvalues)
  416.             r3_real = select_one_nearest(r3_ideal, r_pvalues)
  417.             print(' nearest values: r2={:.1f}, r3={:.1f}, c2={:1.1e}'.format(r2_real, r3_real, c2_real))
  418.  
  419.             #wc_real = 1.0/math.sqrt(c1*c2_real*r2_real*r3_real)
  420.             #k_real = (-r2_real)/r1
  421.             #q_real = 1.0 / (wc_real * c1 * ((1.0-k_real)*r3_real + r2_real))
  422.             (wc_real,k_real,q_real) = calc_mfb2_parameters(r1,r2_real,r3_real,c1,c2_real)
  423.  
  424.             print(' real parameters: q={:.4f}, k={:.4f}, fc={:.1f}'.format(q_real,k_real,wc_real/(2*math.pi)) )
  425.  
  426.             #f_values += [ (r1,r2_real,r3_real, c1, c2_real, wc_real, k_real, q_real) ]
  427.             f_values += [ {'r1':r1,'r2':r2_real,'r3':r3_real, 'c1':c1, 'c2':c2_real, 'wc':wc_real, 'k':k_real, 'q':q_real} ]
  428.  
  429.  
  430.    
  431.     print(f_values)
  432.  
  433.     # sort values by cutoff freq match
  434.     print('\n\n\n sorted by cutoff freq:')
  435.     for e in  sorted(f_values, key = lambda tup: abs((tup['wc']-wc_set)/wc_set) ):
  436.         print('  c1={:1.1e}, r1={:.1f}, r2={:.1f}, r3={:.1f}, c2={:1.1e}, q={:.4f}, k={:.4f}, fc={:.1f}'.format(e['c1'],e['r1'],e['r2'],e['r3'],e['c2'],e['q'],e['k'],e['wc']/(2*math.pi)) )
  437.         (p_wc, p_k, p_q) = randomize_mfb2(e['r1'],e['r2'],e['r3'],e['c1'],e['c2'])
  438.         print('   percent_fc={:.2f}%, percent_k={:.2f}%, percent_q={:.2f}%'.format(100*p_wc,100*p_k,100*p_q))
  439.  
  440.     # sort values by q match
  441.     print('\n\n\n sorted by q:')
  442.     for e in  sorted(f_values, key = lambda tup: abs((tup['q']-q_set)/q_set) ):
  443.         print('  c1={:1.1e}, r1={:.1f}, r2={:.1f}, r3={:.1f}, c2={:1.1e}, q={:.4f}, k={:.4f}, fc={:.1f}'.format(e['c1'],e['r1'],e['r2'],e['r3'],e['c2'],e['q'],e['k'],e['wc']/(2*math.pi)) )
  444.  
  445.  
  446.  
  447. def main():
  448.  
  449.     #calc_mfb_lowpass()
  450.  
  451.     #calc_filter()
  452.  
  453.     print(" #### AY ####")
  454.     calc_filter( wc_set=2*math.pi*30000, q_set=1.0, k_set=(-1.75), r1_range=[20.0,30.0], r1_exp=1e3, r1_div=3.0, r_pvalues='e24', c1_range=[33.0,4700.0], c1_exp=1e-12, c_pvalues='e12')
  455.  
  456. #    print(" #### beep ####")
  457. #    calc_filter( wc_set=2*math.pi*30000, q_set=1.0, k_set=(-0.388), r1_range=[4.7,10.0], r1_exp=1e3, r1_div=1.0, r1_add=750, r_pvalues='e24', c1_range=[33.0,4700.0], c1_exp=1e-12, c_pvalues='e12')
  458.  
  459.  
  460. if __name__=="__main__":
  461.     main()
  462.  
  463.  
  464.  
  465.