#!/usr/bin/python
#>>--<<..>>--<<..>>--<<..>>--<<..>>--<<..>>--<<..>>--<<..>>
#
# author:      Luciano Augusto Kruk
# website:     www.kruk.eng.br
#
# description: Calculates the values for the resistors
#               given R2. The circuit is available at
#               Lipo-protection-LT1389_0699_Mag.pdf
#
# +-----------------------------------------------------------------------------------------------------------------+
# |                                                                                                                 |
# |                                                                                             PMOS                |
# |        ........................................................................................   ........o     |
# |        c                         c                  o                                     |   ^   |       o     |
# |        c                         c                 MMM                                    ccccocccc       o     |
# |        c                         c             R1  MMM        c                               o           o     |
# |        c                        MMM                MMM        c c.                            o          .o     |
# |  Vi    o                    R3  MMM                 o         o  cc.                          o           o     |
# |  -------------                  MMM             Vb  oocc...ccco +  cc.                        c           o     |
# |     ------                      MMM                 c         o      .cc    Va     MMMMMM     c           o     |
# |  -------------                   c                  c         o        .OcccccoMoccMMMMMMccccc.          cMc    |
# |     ------                      cOc.ccccccccccc.....oc.. .....o -    .cc.      O   MMMMMM               .MO.    |
# |        o                         o                  o         o    .c.         o     R6                 oWMc Rc |
# |        c                         o                 vvv        o .c.            o                        oOO.    |
# |        c                        MMM            Vd   M         cc               o                          o     |
# |        o                    R4  MMM                MMM        c                o                          o     |
# |        o                        MMM                 o           MMMMMM        cO                          c     |
# |        o                        MMM                 o...........MMMMMMo........c                          c     |
# |        o                         c             R2  MMM          MMMMMM                                    cc    |
# |       .o                         c                 MMM            R5                                      .c    |
# |       .o                         o                  o                                                     .o    |
# |       ..............................................l.......................................................    |
# |                                                                                                                 |
# |                                                                                                                 |
# +-----------------------------------------------------------------------------------------------------------------+
# 
#>>--<<..>>--<<..>>--<<..>>--<<..>>--<<..>>--<<..>>--<<..>>

from scipy.optimize import fsolve
import sys
import numpy as np
import matplotlib;
import matplotlib.pyplot  as plt;
import math

#>>--<<..>>--<<..>>--<<..>>--<<..>>--<<..>>--<<..>>--<<..>>

def fn_paral(a,b):
    return (1./((1./a) + (1./b)))

#>>--<<..>>--<<..>>--<<..>>--<<..>>--<<..>>--<<..>>--<<..>>

# Voltage at Vb when Va=LOW(vt)
def fn_vb1(R1,R2,R5,vi,vd,vt):

    k = (R1/R2) * (1. + (R2/R5))
    vb1 = (vi+(k*vd)+(R1*vt/R5))/(1.+k)

    return vb1

# Voltage at Vb when Va=HIGH(vT)
def fn_vb2(R1,R2,R5,vi,vd,vT):

    alfa = (1.+(R1/R2))/R1
    beta = (R2+(R2*R5/R1))/(1.+(alfa*R5))
    vb2  = (vd*(1.+(beta*(alfa-(1./R2)))-(R2/R1))) + (beta*vi/R2) + (vT*((R2/R1)-alfa))

    return vb2

def equations(var, PARAM):
    # when the discharge reaches vi_low, va-->HIGH  [OFF]
    # when the charge reaches vi_high, va-->LOW     [ON]
    # vt = residual level from rail-to-rail at GND
    # vT = residual level from rail-to-rail at V_bat

    [R1,R5,k] = var
    [R2,imin,vi_low,vi_high,vd,vt,vT] = PARAM

    eq = (
            fn_vb1(R1,R2,R5,vi_low,vd,vt) - (vi_low*k),
            fn_vb2(R1,R2,R5,vi_high,vd,vT) - (vi_high*k),
            R1 + fn_paral(R2,R5) - ((vi_low-vd)/imin)
    )

    return eq

#>>--<<..>>--<<..>>--<<..>>--<<..>>--<<..>>--<<..>>--<<..>>

def fn_save_log(data, x_rec):
    nb    = len(x_rec)
    x_rec = np.asarray(x_rec)
    x_rec = x_rec.reshape((1,nb))

    if (len(data) == 0):
        data = x_rec.copy();
    else:
        data = np.vstack((data, x_rec))

    return data

#>>--<<..>>--<<..>>--<<..>>--<<..>>--<<..>>--<<..>>--<<..>>

def fn_main():
    R2_log10 = np.log10(np.linspace(1,300e3,1000))
    data     = [];
    vd       = 1.25 # [V]
    vi_low   = 6.6 # [V]
    vi_high  = 6.9 # [V]
    vt       = 0.1 # [V] LOW to rail
    vT       = 0.1 # [V] rail to HIGH
    imin     = 30e-6; # [A]

    #%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#
    # put here the adopted solution:
    if True:
        R2      = 15e3
        PARAM   = [R2,imin,vi_low,vi_high,vd,vt,vT]
        (res,infodict,ier,msg) = fsolve(equations, (1e5,1e5,1), args=PARAM, full_output=True, maxfev=int(1e6), xtol=1e-13)
        [R1,R5,k] = res
        print ier
        print msg
        print res

        print "############################################################"
        if True: # replacement for commercial values:
            R1       = 163.5e3
            R2       = 15e3
            R5       = 2.2e6
            res      = (R1,R5)
            PARAM[0] = R2

        if (ier == 1):
            print "for R2 = %1.3f [kOhm]," % (R2/1e3)
            print "R1 = %1.3f [kOhm]; R5 = %1.3f [MOhm]" % (R1/1000., R5/1e6)
            print "vb1 = %1.2f [V]" % (fn_vb1(R1,R2,R5,vi_low,vd,vt))
            print "vb2 = %1.2f [V]" % (fn_vb2(R1,R2,R5,vi_high,vd,vT))

            # relation between R3 and R4:
            aux = fn_vb1(R1,R2,R5,vi_low,vd,vt);
            aux = (vi_low - aux) / aux
            print "R3 = %1.2e * R4" % aux
            print "R4 = %1.2e * R3" % (1./aux)
        else:
            print "msg = %s" % msg
        print "############################################################"
        #sys.exit(0);

    #%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#
    # calculations:
    for r2_log10 in R2_log10:
        R2    = 10.**r2_log10

        PARAM = [R2,imin,vi_low,vi_high,vd,vt,vT]
        (res,infodict,ier,msg) = fsolve(equations, (1e5,1e5,1), args=PARAM, full_output=True, maxfev=int(1e6), xtol=1e-13)
        [R1,R5,k] = res

        if (ier == 1) and (R1>0) and (R5>0):
            print "R1 = %1.3f [kOhm]; R2 = %1.3f [kOhm]; R5 = %1.3f [MOhm]" % (R1/1000., R2/1e3, R5/1e6)

            x_rec = (R1, R2, R5, fn_vb1(R1,R2,R5,vi_low,vd,vt), fn_vb2(R1,R2,R5,vi_high,vd,vT), 0,0)
            data = fn_save_log(data, x_rec)

    # pictures:
    fig = 0;

    #---- new figure:
    fig = fig + 1; pfig = plt.figure(fig); plt.clf();
    pfig.canvas.set_window_title("vb x R")
    for i in range(3):
        plt.subplot(1,3,i+1)
        resistor = "R%d" % ([1,2,5][i])
        plt.plot(data[:,i]/1e3, data[:,3], data[:,i]/1e3, data[:,4], hold=False);
        plt.legend(('vb1', 'vb2'))
        plt.xlabel("%s [kOhm]" % resistor)
        plt.grid();

    #-- new figure:
    fig = fig + 1; pfig = plt.figure(fig); plt.clf();
    pfig.canvas.set_window_title("(R1,R5) x R2")
    plt.subplot(2,1,1)
    plt.plot(data[:,1]/1e3, data[:,0]/1e3, linestyle='None', marker='x', markersize=5, hold=False);
    plt.ylabel('R1 [kOhm]');
    plt.xlabel("R2 [kOhm]")
    plt.grid();

    plt.subplot(2,1,2)
    plt.plot(data[:,1]/1e3, data[:,2]/1e6, linestyle='None', marker='x', markersize=5, hold=False)
    plt.ylabel('R5 [MOhm]');
    plt.xlabel("R2 [kOhm]")
    plt.grid();

    return data

#>>--<<..>>--<<..>>--<<..>>--<<..>>--<<..>>--<<..>>--<<..>>
if (__name__ == "__main__"):
    data = fn_main()
    plt.show(block=False);
#>>--<<..>>--<<..>>--<<..>>--<<..>>--<<..>>--<<..>>--<<..>>

