# -*- coding: utf-8 -*-


VERSION = 20180914


'''
DESCRIPTION
    This LPDA calculator is based on the design procedure 
    as described by L. B. Cebik, W4RNL (SK) 
    in the 21st edition of The ARRL Antenna Handbook.


USAGE
    See: http://hamwaves.com/lpda/en/index.html


COPYRIGHT
    Copyright (C) 2014-2018 Serge Y. Stroobandt

    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
    ON4AA
    xdg-open mailto:$(echo c2VyZ2VAc3Ryb29iYW5kdC5jb20K |base64 -d)
'''


### IMPORTS ###

from browser import document
from browser.html import OPTION
from math import floor, log, sqrt
import time


### GLOBALS ###

c = 299792458.0


### FUNCTIONS ###

def update_range(event):
    id = event.target.id.replace('range.','')
    value = event.target.value
    document[id].value = value
    calculate(event)


def update_diam_N(event):
    diam_N = float(document['diam_N'].value)

    if(document['SI'].checked):
        document['diam_N'].value = '%.2f' % (diam_N * 25.4)

    if(document['imperial'].checked):
        document['diam_N'].value = '%.4f' % (diam_N / 25.4)

    calculate(event)


def subscript(integer):
    return ''.join([chr(8320+int(i)) for i in str(integer)])


def calculate(event):

    document['txt'].clear()
    
    if(document['SI'].checked):
        conv = 1.0
        unit = 'm'
    if(document['imperial'].checked):
        conv = (12 * 0.0254)**-1
        unit = 'ft'
    
    try:
        f_1 = float(document['f_1'].value) * 1E6
        f_n = float(document['f_n'].value) * 1E6
        
        if(f_n >= f_1):

            t = time.time()
            document['txt'] <= 'LPDA — https://hamwaves.com/lpda/ — v{}\n'.format(VERSION)
            document['txt'] <= time.strftime('  LPDA design %Y-%m-%d %H:%M\n', time.localtime(t))

            document['txt'] <= '\nINPUT\n'
            document['txt'] <= '  Lowest frequency f₁ = %s MHz\n' % document['f_1'].value
            document['txt'] <= '  Highest frequency fₙ = %s MHz\n' % document['f_n'].value
        
            if(document['SI'].checked):
                diam_N = float(document['diam_N'].value)*1E-3
                document['txt'] <= '  Diameter of the shortest element ⌀ = %s mm\n' % document['diam_N'].value
            if(document['imperial'].checked):
                diam_N = float(document['diam_N'].value)*0.0254
                document['txt'] <= '  Diameter of the shortest element ⌀ = %s inch\n' % document['diam_N'].value
        
            Zc_in = float(document['Zc_in'].value)
            document['txt'] <= '  Characteristic input impedance Zc_in = %.f Ω\n' % Zc_in

            B = f_n/f_1
            document['B'].value = '%.3f' % B
            
            tau = float(document['tau'].value)
            document['txt'] <= '  Taper τ = %.3f\n' % tau
            sigma_opt = 0.243 * tau - 0.051
            document['range.sigma'].max = '%.3f' % sigma_opt
            document['sigma_opt'].value = '%.3f' % sigma_opt
            sigma = float(document['sigma'].value)
            if(sigma > sigma_opt):
                document['sigma'].value = '%.3f' % sigma_opt
            document['txt'] <= '  Optimal relative spacing σₒₚₜ = %.3f\n' % sigma_opt
            document['txt'] <= '  Chosen relative spacing σ = %.3f\n' % sigma
            
            document['txt'] <= '\nRESULTING DESIGN\n'

            cot_alpha = 4 * sigma * (1 - tau)**-1
            document['cot_alpha'].value = '%.3f' % cot_alpha
            
            B_ar = 1.1 + 7.7 * (1 - tau)**2 * cot_alpha
            document['B_ar'].value = '%.3f' % B_ar
            
            B_S = B * B_ar
            document['B_S'].value = '%.3f' % B_S
            
            N = 1 + log(B_S) / log(tau**-1)
            if(N-floor(N) > 0.3):
                N = int(N) + 1
            else:
                N = int(N)
            document['N'].value = N
            document['txt'] <= '  Number of elements ⌊N⌉ = %d\n' % N
            
            ell = []
            ell_tot = 0.
            document['ell'].clear()
            document['txt'] <= '  Dipole element lengths:\n'
            lambda_1 = c / f_1
            ell.append(0.5 * lambda_1)
            ell_tot += ell[0]
            document['ell'] <= OPTION('ℓ%s = %.3f %s' % ('₁', conv * ell[0], unit), value = 1)
            document['txt'] <= '    dipole ℓ%s = %.3f %s\n' % ('₁', conv * ell[0], unit)
            for i in range(1,N):
                ell.append(tau * ell[i-1])
                ell_tot += ell[i]
                document['ell'] <= OPTION('ℓ%s = %.3f %s' % (subscript(i+1), conv * ell[i], unit), value = i+1)
                document['txt'] <= '    dipole ℓ%s = %.3f %s\n' % (subscript(i+1), conv * ell[i], unit)
            document['ell'] <= OPTION('ℓₜₒₜ = %.3f %s' % (conv * ell_tot, unit))
            document['txt'] <= '  Sum of all dipole lengths ℓₜₒₜ = %.3f %s\n' % (conv * ell_tot, unit)
            
            d = []
            L = 0.
            document['d'].clear()
            document['txt'] <= '\n  Distances between the element centres\n'
            document['txt'] <= '  and their position along the boom:\n'
            for i in range(N-1):
                d.append(0.5 * (ell[i] - ell[i+1]) * cot_alpha)
                L += d[i]
                document['d'] <= OPTION('d%s,%s = %.3f %s, i.e. ℓ%s @ %.3f %s' % (subscript(i+1), subscript(i+2), conv * d[i], unit, subscript(i+2), conv * L, unit), value = i+1)
                document['txt'] <= '    d%s,%s = %.3f %s, i.e. ℓ%s @ %.3f %s\n' % (subscript(i+1), subscript(i+2), conv * d[i], unit, subscript(i+2), conv * L, unit)
            document['L'].value = '%.3f %s' % (conv * L, unit)
            document['txt'] <= '  Boom length L = %.3f %s\n' % (conv * L, unit)
            
            ell_Z_term = 0.125 * lambda_1
            document['ell_Z_term'].value = '%.3f %s' % (conv * ell_Z_term, unit)
            document['txt'] <= '\n  Length of the terminating stub ℓ_Zterm = %.3f %s\n' % (conv * ell_Z_term, unit)
            
            #Cebik
            Zc_N = 120 * (log(ell[N-1] / diam_N) - 2.25)
            document['Zc_N'].value = '%.1f Ω' % Zc_N
            
            sigma_mean = sigma / sqrt(tau)
            Zc_feed = Zc_in**2 * (8 * sigma_mean * Zc_N)**-1
            Zc_feed += Zc_in * sqrt( (Zc_in**2 * 0.015625 * sigma_mean**-2 * Zc_N**-2) +1)
            document['Zc_feed'].value = '%.1f Ω' % Zc_feed
            document['txt'] <= '  Required characteristic impedance of the feeder\n'
            document['txt'] <= '  connecting the elements Zc_feed = %.1f Ω\n' % Zc_feed

            document['txt'] <= '\nDONATE\n'
            document['txt'] <= '  If this calculator proved any useful to you,\n'
            document['txt'] <= '  please, consider making a one-off donation\n'
            document['txt'] <= '  towards keeping me and the server up and running.\n'
            document['txt'] <= '  Thank you!'
        
        else:
            raise ValueError
    
    except:
        document['txt'].clear()
        outputs = ['B', 'sigma_opt', 'cot_alpha', 'B_ar', 'B_S', 'N', 'L', 'ell_Z_term', 'Zc_N', 'Zc_feed']
        for i in range(len(outputs)):
            document[outputs[i]].value = ''
        document['ell'].clear()
        document['d'].clear()


### MAIN ###

document['brython'].style.display = 'initial'
calculate(None)


### EVENT HANDLERS ###

inputs = ['f_1', 'f_n', 'diam_N', 'Zc_in']
for i in range(len(inputs)):
    document[inputs[i]].bind('focus', calculate)
    document[inputs[i]].bind('input', calculate)

document['SI'].bind('change', update_diam_N)
document['imperial'].bind('change', update_diam_N)

document['range.tau'].bind('change', update_range)
document['range.sigma'].bind('change', update_range)
