Subversion Repositories pentevo

Rev

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