PyFoil 1.2

Ecco la nuova versione della mia applicazione PyFoil, sviluppata in Python per Symbian S60.

Purtroppo a causa della mancanza di tempo non sono riuscito a completarla e sono presenti alcuni bug che segnalo stesso in questa pagina:

  • I dati sulla pressione e la temperatura nella stratosfera non vengono calcolati correttamente
  • Il calcolo del centro aerodinamico dell’ala non è corretto in caso di angolo di freccia
  • Il calcolo del coefficiente di momento non è corretto
  • È possibile settare i dati del piano di coda orizzontale, ma questi non vengono ancora utilizzati per fare calcoli

L’applicazione è comunque in grado di calcolare svariati parametri geometrici e aerodinamici sull’ala, impostandone caratteristiche alla radice e all’estremità.

Segue il codice del programma.

import e32
import graphics
import appuifw
from random import randint
from math import sqrt, sin, cos, tan, atan, log10, ceil, pi, e as E

def draw(r=None):
    """Draw the buffer on the canvas"""
    if buffer:
        c.blit(buffer)

c = appuifw.Canvas(redraw_callback=draw)
buffer = graphics.Image.new(c.size)
appuifw.app.body = c
width, height = c.size
# airfoil contains [NACA, digit, (t, m, p)]
# wing_G contains geometrical parameters about wing (b, br, ct, boh)
# wing_A contains aerodynamical parameters about wing (boh, ancora più boh)
airfoil = [None, None, [0, 0, 0]]
wing_G = [10., # Wingspan
          1.6, # Chord root
          0.8, # Chord tip
          15., # Sweep angle
          3.0, # Wing incidence
          0.9, # Efficiency number
         -3.0] # Twist angle

wing_A = [
        # Root
         [0.11,  # Alfa lift coefficient
           -0.07, # Zero-lift moment coefficient
           -1.0,  # Alfa zero lift
           0.25], # Aerodynamic center position
        # Tip
          [0.105, # Clat
           -0.08, # Cm0t
           -2.5,  # azlt
           0.23]  # xact
        ]
fuselage = [-0.05,  # Zero lift moment coefficient
            0.0035] # Alpha moment coefficient
tail = [[], # Horizontal
        []] # Vertical

def derivative(func, x):
    """Derivates a function in a point"""
    return (func(x) - func(x+0.001))/0.001

def integrate(func, interval):
    """Trapezoidal numerical integration"""
    a, b = interval
    a = float(a)
    b = float(b)
    # Splits interval in 500 points per unit
    if abs(b - a) < 1e-5:
        return 0
    points = (b - a)*500.0
    increment = (b - a)/points
    area = 0.0
    for i in xrange(points):
        i = float(i)
        x = b * i/points + a * (1.0 - i/points)
        # Jump a possibile discontinuity
        try:
            area += (func(x+increment) + func(x)) * increment / 2.0
        except:
            pass
    return area

def mean_line(x, airfoil):
    """Airfoil mean line function"""
    digit = airfoil[1]
    t, m, p = airfoil[2]
    if digit == 4:
        if p < 1e-5:
            return 0
        elif x <= p:
            return m / (p**2) * (2*p*x - x**2)
        elif x > p:
            return m * (1 - 2*p + 2*p*x - x**2) / ((1 - p)**2)
    elif digit == 5:
        if x <= m:
            return p / 6 * (x**3 - 3*m*x**2 + m**2 * (3 - m)*x)
        elif x > m:
            return p * m**3 / 6*(1 - x)

def thickness(x, airfoil):
    """Airfoil thickness function"""
    t = airfoil[2][0]
    return t / 0.2 * (+0.2969 * x**0.5 +
                      -0.1260 * x**1 +
                      -0.3516 * x**2 +
                      +0.2843 * x**3 +
                      -0.1015 * x**4)

def NACA_set():
    """Set NACA to operate"""
    global airfoil
    NACA = appuifw.query(u'Insert 4-5 digit NACA', 'number')
    if NACA:
        digit = ceil(log10(NACA))
        # Digit correction for 00XX and 000X
        if digit in [1, 2, 3]: digit = 4
        t = (NACA%100)/100. # Thickness
        if digit == 4:
            m = (NACA/1000)/100. # Max camber
            p = (NACA/100-NACA/1000*10)/10. # Max camber position
            airfoil = [NACA, digit, (t, m, p)]
            NACA_plot()
        elif digit == 5:
            mean_line_datas = {210:[0.0580, 361.4],
                               220:[0.1260, 51.64],
                               230:[0.2025, 15.957],
                               240:[0.2900, 6.643],
                               250:[0.3910, 3.230]}
            try:
                m = mean_line_datas[NACA/100][0]
                p = mean_line_datas[NACA/100][1]
                airfoil = [NACA, digit, (t, m, p)]
                NACA_plot()
            except KeyError:
                appuifw.note(u'NACA not supported!', 'error')
        else:
            appuifw.note(u'NACA must be 4 or 5 digit!', 'error')   
    else:
        appuifw.note(u'NACA must be 4 or 5 digit!', 'error')
    
def NACA_plot():
    """Plots a NACA"""
    if not airfoil[0]:
        NACA_set()
        if not airfoil[0]:
            return
    buffer.clear()
    # NACA parameters
    NACA, digit, N = airfoil
    font = (None, 30)
    color0 = (0, 0, 0) # Text color
    color1 = (0, 0, 255) # Airfoil color
    color2 = (255, 0, 0) # Meanline color
    color3 = (100, 100, 100) # Radius color
    # Draws the axes
    s_width = width - 10 # Scaled width, for the border
    y0 = height/2 # Origin of axes
    buffer.line((0, y0, width, y0), outline=color0)
    # Draws the scale
    unit = 10 # Axis will be divided into %unit part
    for u in range(11):
        buffer.line((5 + u * s_width / unit, y0 - 2,
                     5 + u * s_width / unit, y0 + 2),
                     outline=color0)
    # Unit legend
    buffer.line((10, 2*y0 - 20,
                 10 + s_width/unit, 2*y0 - 20),
                 outline=color0)
    buffer.text((20 + s_width/unit, 2*y0 - 15),
                 u'%d%% of the chord' % (100/unit),
                 fill=color0)
    # Displays infos about the airfoil
    radius = 1.1019 * N[0]**2
    radius_pos = (5, y0 - radius*s_width,
                  5 + radius*s_width*2, y0 + radius*s_width)
    buffer.ellipse(radius_pos, outline=color3)
    buffer.text((10, 30), u'NACA %0#4d' % NACA, font=font, fill=color0)
    buffer.text((10, 55), u'LE radius: %.4f' % radius, fill=color0)
    # Plot
    for x in xrange(s_width):
        x = float(x)/s_width
        # xx is an increment of x to calculate next point of each segment
        xx = (x * s_width + 1) / s_width
        # Meanline
        xM_1 = 5 + x * s_width
        yM_1 = y0 - mean_line(x, airfoil) * s_width
        xM_2 = 5 + xx * s_width
        yM_2 = y0 - mean_line(xx, airfoil) * s_width
        buffer.line((xM_1, yM_1, xM_2, yM_2), outline=color2)
        # Airfoil (U: Upper, L: Lower, 1-2 are 1st and 2nd point of the line)
        teta = atan(derivative(lambda x: mean_line(x, airfoil), x))
        xU_1 = 5 + (x - thickness(x, airfoil)*sin(teta)) * s_width
        yU_1 = y0 - (mean_line(x, airfoil) - 
                    thickness(x, airfoil)*cos(teta)) * s_width
        xL_1 = 5 + (x + thickness(x, airfoil)*sin(teta)) * s_width
        yL_1 = y0 - (mean_line(x, airfoil) +
                    thickness(x, airfoil)*cos(teta)) * s_width
        xU_2 = 5 + (xx - thickness(xx, airfoil)*sin(teta)) * s_width
        yU_2 = y0 - (mean_line(xx, airfoil) -
                    thickness(xx, airfoil)*cos(teta)) * s_width
        xL_2 = 5 + (xx + thickness(xx, airfoil)*sin(teta)) * s_width
        yL_2 = y0 - (mean_line(xx, airfoil) +
                    thickness(xx, airfoil)*cos(teta)) * s_width
        buffer.line((xU_1, yU_1, xU_2, yU_2), outline=color1, width=2)
        buffer.line((xL_1, yL_1, xL_2, yL_2), outline=color1, width=2)
    draw()
    
def NACA_export():
    """Export NACA plot as image"""
    if not airfoil[0]:
        NACA_set()
        if not airfoil[0]:
            return
    new_width = appuifw.query(u'Image width (px)', 'number', 800)
    new_height = new_width / 1.4
    image = graphics.Image.new((new_width, new_height))
    image.clear()
    # NACA parameters
    NACA, digit, N = airfoil
    font = (None, 30)
    color0 = (0, 0, 0) # Text color
    color1 = (0, 0, 255) # Airfoil color
    color2 = (255, 0, 0) # Meanline color
    color3 = (100, 100, 100) # Radius color
    # Draws the axes
    s_width = new_width - 10 # Scaled width, for the border
    y0 = new_height/2 # Origin of axes
    image.line((0, y0, new_width, y0), outline=color0)
    # Draws the scale
    unit = 10 # Axis will be divided into %unit part
    for u in range(11):
        image.line((5 + u * s_width / unit, y0 - 2,
                    5 + u * s_width / unit, y0 + 2),
                    outline=color0)
    # Unit legend
    image.line((10, 2*y0 - 20,
                 10 + s_width/unit, 2*y0 - 20),
                 outline=color0)
    image.text((20 + s_width/unit, 2*y0 - 15),
                 u'%d%% of the chord' % (100/unit),
                 fill=color0)
    # Displays infos about the airfoil
    radius = 1.1019 * N[0]**2
    radius_pos = (5, y0 - radius*s_width,
                  5 + radius*s_width*2, y0 + radius*s_width)
    image.ellipse(radius_pos, outline=color3)
    image.text((10, 30), u'NACA %0#4d' % NACA, font=font, fill=color0)
    image.text((10, 55), u'LE radius: %.4f' % radius, fill=color0)
    # Plot
    for x in xrange(s_width):
        x = float(x)/s_width
        # xx is an increment of x to calculate next point
        xx = (x * s_width + 1) / s_width
        # Meanline
        xM_1 = 5 + x * s_width
        yM_1 = y0 - mean_line(x, airfoil) * s_width
        xM_2 = 5 + xx * s_width
        yM_2 = y0 - mean_line(xx, airfoil) * s_width
        image.line((xM_1, yM_1, xM_2, yM_2), outline=color2)
        # Airfoil (U: Upper, L: Lower, 1-2 are 1st and 2nd point of the line)
        teta = atan(derivative(lambda x: mean_line(x, airfoil), x))
        xU_1 = 5 + (x - thickness(x, airfoil)*sin(teta)) * s_width
        yU_1 = y0 - (mean_line(x, airfoil) - 
                    thickness(x, airfoil)*cos(teta)) * s_width
        xL_1 = 5 + (x + thickness(x, airfoil)*sin(teta)) * s_width
        yL_1 = y0 - (mean_line(x, airfoil) +
                    thickness(x, airfoil)*cos(teta)) * s_width
        xU_2 = 5 + (xx - thickness(xx, airfoil)*sin(teta)) * s_width
        yU_2 = y0 - (mean_line(xx, airfoil) -
                    thickness(xx, airfoil)*cos(teta)) * s_width
        xL_2 = 5 + (xx + thickness(xx, airfoil)*sin(teta)) * s_width
        yL_2 = y0 - (mean_line(xx, airfoil) +
                    thickness(xx, airfoil)*cos(teta)) * s_width
        image.line((xU_1, yU_1, xU_2, yU_2), outline=color1, width=2)
        image.line((xL_1, yL_1, xL_2, yL_2), outline=color1, width=2)
    file_name = appuifw.query(u'Insert file name', 'text', u'.png')
    file_path = u'C:\\%s' % file_name
    image.save(file_path)
    del image
    appuifw.note(u'Image saved at C:\\%s' % file_name, 'info')
    # Ask if want to send the file
    if appuifw.query(u'Send the file via BT?', 'query'):
        try:
            import btsocket as socket
        except ImportError:
            import socket
        address, services = socket.bt_obex_discover()
        channel = services.items()[0][1]
        try:
            socket.bt_obex_send_file(address, channel, file_path)
        except error:
            appuifw.note(error.decode('utf-8'), 'error')

def ISA(z):
    """International standard atmosphere"""
    T_sl = 288.15 # Kelvin
    p_sl = 101325.0 # Pascal
    rho_sl = 1.225 # kg/m^3
    # Air gas constant: 287 J / (kg * K)
    T = T_sl - 6.5 * (z/1000.0) # Thermal gradient: -6.5 K/km
    p = p_sl * (T/T_sl) ** (9.81 / 287 / 6.5e-3)
    rho = rho_sl * (T/T_sl) ** (9.81 / 287 / 6.5e-3 - 1)
    # Troposphere
    #if z < 11000:
    #    T = T_sl - 6.5 * (z/1000.0) # Thermal gradient: -6.5 K/km
    #    p = p_sl * (T/T_sl) ** (9.81 / 287 / 6.5e-3)
    #    rho = rho_sl * (T/T_sl) ** (9.81 / 287 / 6.5e-3 - 1)
    ## Stratosphere
    #elif z >= 11000 and z < 20000:
    #    T = 216.65
    #    p = 2270 * E ** (-9.81 / 287 / 6.5e-3 * (z-11000))
    #    rho = 0.2978 * E ** (-9.81 / 287 / 6.5e-3 * (z-11000))
    #elif z >= 20000:
    #    # Up 20000 m thermal gradient is approximated
    #    T = 216.65 + 0.98 * (z-20000)/1000.0 # Thermal gradient: ~ 0.98 K/km
    #    p = 2270 * E ** (-9.81 / 287 / 6.5e-3 * (z-11000))
    #    rho = 0.2978 * E ** (-9.81 / 287 / 6.5e-3 * (z-11000))
    return (T, p, rho)
    
def Reynolds():
    """Shows a form to calculate dimensionless quantity"""
    fields = [(u'Speed [m/s]', 'float', 0.0),
              (u'Density [kg/m^3]', 'float', 0.0),
              (u'D. viscosity [Pa*s]', 'float', 0.0),
              (u'Linear dimension [m]', 'float', 0.0)]
    flag = appuifw.FFormEditModeOnly + appuifw.FFormDoubleSpaced
    form = appuifw.Form(fields, flag)
    form.execute()
    # Result
    speed, density, viscosity, linear_d = [i[2] for i in list(form)]
    reynolds = density * speed * linear_d / viscosity
    appuifw.query(u'Reynolds:', 'text', unicode(reynolds))
    appuifw.query(u'Reynolds (exp):', 'text', u'%.0e' % reynolds)
    
def Mach():
    """Shows a form to calculate dimensionless quantity"""
    fields = [(u'Speed [m/s]', 'float', 0.0),
              (u'Sound speed [m/s]', 'float', 0.0),
              (u'* Temperature [degC]', 'float', 0.0),
              (u'* Altitude [m]', 'float', 0.0)]
    flag = appuifw.FFormEditModeOnly + appuifw.FFormDoubleSpaced
    form = appuifw.Form(fields, flag)
    appuifw.note(u'You may use temp. or alt. instead of sound speed', 'info')
    form.execute()
    # Result
    speed, sound_speed, temperature, altitude = [i[2] for i in list(form)]
    if sound_speed < 1e-5:
        if temperature != 0.0:
            # Air gas constant: 287 J / (kg * K)
            sound_speed = (1.4 * 287 * (273.15+temperature)) ** 0.5
        elif altitude != 0.0:
            sound_speed = (1.4 * 287 * ISA(altitude)[0]) ** 0.5
        else:
            appuifw.note(u'Not enough parameters', 'error')
    mach = speed / sound_speed
    appuifw.query(u'Mach:', 'text', unicode(mach))
    
def Froude():
    """Shows a form to calculate dimensionless quantity"""
    fields = [(u'Speed [m/s]', 'float', 0.0),
              (u'\u0394 z [m]', 'float', 0.0),
              (u'Gravity [m/s^2]', 'float', 9.81)]
    flag = appuifw.FFormEditModeOnly + appuifw.FFormDoubleSpaced
    form = appuifw.Form(fields, flag)
    form.execute()
    # Result
    speed, linear_d, gravity = [i[2] for i in list(form)]
    froude = speed * speed / gravity / linear_d
    appuifw.query(u'Froude:', 'text', unicode(froude))
    
def wing_set(type, par):
    """
    Set the geometrical or aerodynamical parameters for the wing
    type can be:
        - 'geom': geometrical parameters
        - 'aero_r': aerodynamic of root chord
        - 'aero_t': aerodynamic of tip chord
    
    wing_G contains:
        - b: wingspan
        - cr: chord at root
        - ct: chord at tip
        - sweep: sweep angle from leading edge
        - iw: angle of incidence of wing (at root chord)
        - ew: efficiency number for non-elliptical wings
        - eps: angle of twist
        
    wing_A cointains:
        - wing_A[0]: aerodynamical parameters at root chord
        - wing_A[1]: aerodynamical parameters at tip chord
    parameters ending with *r are referred to the root chord
    parameters ending with *t are referred to the tip chord
    """
    global wing_G, wing_A, fuselage, tail
    # Set geometrical parameters
    if type == 'geom':
        # Set all parameters
        if par == 'all':
            fields = [(u'Wingspan [m]', 'float', wing_G[0]),
                      (u'Chord root [m]', 'float', wing_G[1]),
                      (u'Chord tip [m]', 'float', wing_G[2]),
                      (u'Sweep angle [deg]', 'float', wing_G[3]),
                      (u'Wing incidence [deg]', 'float', wing_G[4]),
                      (u'Efficiency number [ ]', 'float', wing_G[5]),
                      (u'Twist angle [deg]', 'float', wing_G[6])]
            flag = appuifw.FFormEditModeOnly + appuifw.FFormDoubleSpaced
            form = appuifw.Form(fields, flag)
            form.execute()
            # Result
            wing_G = b, cr, ct, sweep, iw, ew, eps = [i[2] for i in list(form)]
        # Set a specific parameter
        elif par == 'wingspan':
            b = appuifw.query(u'Set wingspan [m]:', 'float', wing_G[0])
            wing_G[0] = b
        elif par == 'chords':
            cr = appuifw.query(u'Set root chord [m]:', 'float', wing_G[1])
            ct = appuifw.query(u'Set tip chord [m]:', 'float', wing_G[2])
            wing_G[1] = cr
            wing_G[2] = ct
        elif par == 'sweep':
            sweep = appuifw.query(u'Set sweep [deg]:', 'float', wing_G[3])
            wing_G[3] = sweep
        elif par == 'incid':
            incidence = appuifw.query(u'Set wing incidence [deg]:',
                                      'float', wing_G[4])
            wing_G[4] = incidence
        elif par == 'effic':
            effic = appuifw.query(u'Set efficiency number [ ]:',
                                  'float', wing_G[5])
            wing_G[5] = effic
        elif par == 'twist':
            twist = appuifw.query(u'Set twist angle [deg]:',
                                  'float', wing_G[6])
            wing_G[6] = twist
        wing_draw()
    # Set aerodynamical parameters for root
    elif type == 'aero_r':
        # Set all parameters
        if par == 'all':
            fields = [(u'Alfa lift coeff. [1/deg]', 'float', wing_A[0][0]),
                      (u'Zero-lift moment coeff. [ ]', 'float', wing_A[0][1]),
                      (u'Alfa zero lift [deg]', 'float', wing_A[0][2]),
                      (u'Aero. center pos. [of Cr]', 'float', wing_A[0][3])]
            flag = appuifw.FFormEditModeOnly + appuifw.FFormDoubleSpaced
            form = appuifw.Form(fields, flag)
            form.execute()
            # Result
            wing_A[0] = Clar, Cm0r, azlr, xacr = [i[2] for i in list(form)]
        # Set a specific parameter
        elif par == 'copy':
            wing_A[0] = wing_A[1]
        elif par == 'Clar':
            Clar = appuifw.query(u'Set alfa lift coeff. [1/deg]:',
                                 'float', wing_A[0][0])
            wing_A[0][0] = Clar
        elif par == 'Cm0r':
            Cm0r = appuifw.query(u'Set zero-lift moment coeff. [ ]:',
                                 'float', wing_A[0][1])
            wing_A[0][1] = Cm0r
        elif par == 'azlr':
            azlr = appuifw.query(u'Set alfa zero lift [deg]:',
                                 'float', wing_A[0][2])
            wing_A[0][2] = azlr
        elif par == 'xacr':
            xacr = appuifw.query(u'Set aero. center pos. [of Cr]:',
                                 'float', wing_A[0][3])
            wing_A[0][3] = xacr
    # Set aerodynamical parameters for tip
    elif type == 'aero_t':
        # Set all parameters
        if par == 'all':
            fields = [(u'Alfa lift coeff. [1/deg]', 'float', wing_A[1][0]),
                      (u'Zero-lift moment coeff. [ ]', 'float', wing_A[1][1]),
                      (u'Alfa zero lift [deg]', 'float', wing_A[1][2]),
                      (u'Aero. center pos. [of Ct]', 'float', wing_A[1][3])]
            flag = appuifw.FFormEditModeOnly + appuifw.FFormDoubleSpaced
            form = appuifw.Form(fields, flag)
            form.execute()
            # Result
            wing_A[1] = Clat, Cm0t, azlt, xact = [i[2] for i in list(form)]
        # Set a specific parameter
        elif par == 'copy':
            wing_A[1] = wing_A[0]
        elif par == 'Clat':
            Clat = appuifw.query(u'Set alfa lift coeff. [1/deg]:',
                                 'float', wing_A[1][0])
            wing_A[1][0] = Clat
        elif par == 'Cm0t':
            Cm0t = appuifw.query(u'Set Zero-lift moment coeff. [ ]:',
                                 'float', wing_A[1][1])
            wing_A[1][1] = Cm0t
        elif par == 'azlt':
            azlt = appuifw.query(u'Set alfa zero lift [deg]:',
                                 'float', wing_A[1][2])
            wing_A[1][2] = azlt
        elif par == 'xact':
            xact = appuifw.query(u'Set aero. center pos.[of Ct]:',
                                 'float', wing_A[1][3])
            wing_A[1][3] = xact
    # Set fuselage parameters
    if type == 'fuse':
        fields = [(u'Zero-lift Moment coeff. [ ]', 'float', fuselage[0]),
                  (u'Alpha moment coeff. [1/deg]', 'float', fuselage[1])]
        flag = appuifw.FFormEditModeOnly + appuifw.FFormDoubleSpaced
        form = appuifw.Form(fields, flag)
        form.execute()
        # Result
        fuselage = Cm0f, Cmaf = [i[2] for i in list(form)]
    # Set tail parameters
    if type == 'tail_h':
        fields = [(u'Alfa lift coeff. 2D [1/deg]', 'float', tail[0][0]),
                  (u'Wingspan [m]', 'float', tail[0][1]),
                  (u'Surface [m^2]', 'float', tail[0][2]),
                  (u'Efficiency number [ ]', 'float', tail[0][3]),
                  (u'Dinamic pressures ratio [ ]', 'float', tail[0][4])]
        flag = appuifw.FFormEditModeOnly + appuifw.FFormDoubleSpaced
        form = appuifw.Form(fields, flag)
        form.execute()
        # Result
        fuselage = Cm0f, Cmaf = [i[2] for i in list(form)]

def wing_draw():
    """Draws the wing."""
    if not any(wing_G):
        wing_set('geom', 'all')
        if not any(wing_G):
            return
    b, cr, ct, sweep, iw, ew, eps = wing_G
    WC = lambda y: cr + y * (ct - cr) * 2.0/b # Wing chord distribution
    buffer.clear()
    font = (None, 30)
    color0 = (0, 0, 0) # Text color
    color1 = (0, 0, 255) # Wing color
    color2 = (255, 0, 0) # Chords color
    color3 = (150, 150, 150) # Legend color
    s_width = width - 10 # Scaled width, for the border
    # Dimensionless ratio
    if cr > b/2:
        DLR = height/2 / cr
    else:
        DLR = s_width / b
    b, cr, ct = [(i * DLR) for i in (b, cr, ct)]
    # Draws axes
    y0 = height/3 # Origin of axes
    x0 = width/2
    buffer.line((0, y0, width, y0), outline=color0)
    buffer.line((x0, 0, x0, height), outline=color0)
    #  Right leading edge
    RLE = (x0, y0,
              x0 + b/2, y0 + b/2 * tan(sweep*pi/180))
    # Right trailing edge
    RTE = (x0 + b/2, y0 + b/2 * tan(sweep*pi/180) + ct,
             x0, y0 + cr)
    # Left tip chord
    LTC = (x0 - b/2, y0 + b/2 * tan(sweep*pi/180) + ct,
             x0 - b/2, y0 + b/2 * tan(sweep*pi/180))
    # Draws left/right wings
    buffer.polygon(RLE + RTE + LTC, outline=color1, width=2)
    # Calculates the Mean Aerodynamic Chord
    # I divide by DLR, so I can integrate for smaller intervals
    b, cr, ct = [(i / DLR) for i in (b, cr, ct)]
    Sw = 2 * integrate(lambda y: WC(y), (0, b/2))
    MAC = 2 / Sw * integrate(lambda y: WC(y)**2, (0, b/2)) * DLR
    y_MAC = 2 / Sw * integrate(lambda y: y * WC(y), (0, b/2)) * DLR
    b, cr, ct = [(i * DLR) for i in (b, cr, ct)]
    # Draws the MAC
    points = (x0 + y_MAC, y0 + (b/2 - y_MAC) * tan(sweep*pi/180),
              x0 + y_MAC, y0 + (b/2 - y_MAC) * tan(sweep*pi/180) + MAC)
    buffer.line(points, outline=color2, width=2)
    # Shows the legend
    line_MAC = (x0 + y_MAC, y0 + (b/2 - y_MAC) * tan(sweep*pi/180),
                x0 + y_MAC, y0 - 20)
    text_MAC = (x0 + y_MAC, y0 - 20)
    buffer.line(line_MAC, outline=color3)
    buffer.text(text_MAC, u'M.A.C.', fill=color3)
    draw()

def wing_info(type):
    """
    Calculates and shows geometrical informations about the wing.
    type can be:
        - 'geom': shows geometrical info
        - 'aero': shows aerodynamic info
    wing_A cointains:
        - wing_A[0]: aerodynamical parameters at root chord
        - wing_A[1]: aerodynamical parameters at tip chord
    parameters ending with *r are referred to the root chord
    parameters ending with *t are referred to the tip chord
    
    "eps" is the twist angle, while "twist" is the law of twist   
    """
    # Geometrical info
    if type == 'geom':
        if not wing_G: wing_set('geom', 'all')
        b, cr, ct, sweep, iw, ew, eps = wing_G
        WC = lambda y: cr + y * (ct - cr) * 2.0/b # Wing chord distribution
        Sw = 2 * integrate(lambda y: WC(y), (0, b/2))
        AR = b**2 / Sw # Aspect ratio
        TR = ct / cr   # Taper ratio
        MAC = 2 / Sw * integrate(lambda y: WC(y)**2, (0, b/2))
        y_MAC = 2 / Sw * integrate(lambda y: y * WC(y), (0, b/2))
        fields = [
            (u'Aspect ratio [ ]', 'text', u'%.2f' % AR),
            (u'Tape ratio [ ]', 'text', u'%.2f' % TR),
            (u'Wing surface [m^2]', 'text', u'%.2f' % Sw),
            (u'Mean Aerodynamic Chord [m]', 'text', u'%.2f' % MAC),
            (u'y of M.A.C. [% of wing]', 'text', u'%.1f' % (y_MAC * 200 / b))]
        flag = appuifw.FFormDoubleSpaced
        form = appuifw.Form(fields, flag)
        form.execute()
    # Aerodynamical info
    elif type == 'aero':
        if not wing_A: wing_set('aero', 'all')
        # Load geometric and aerodynamic parameters
        b, cr, ct, sweep, iw, ew, eps = wing_G
        WC = lambda y: cr + y * (ct - cr) * 2.0/b # Wing chord distribution
        Clar, Cm0r, azlr, xacr = wing_A[0]
        Clat, Cm0t, azlt, xact = wing_A[1]
        # Laws of variation from root to tip
        Cla = lambda y: Clar + y * (Clat - Clar) / (b/2)
        Cm0 = lambda y: Cm0r + y * (Cm0t - Cm0r) / (b/2)
        azl = lambda y: azlr + y * (azlt - azlr) / (b/2)
        xac = lambda y: xacr + y * (xact - xacr) / (b/2)
        twist = lambda y: y * eps / (b/2)
        # Lift coefficient of 2D wing (without downwash), weighted mean
        par = (b, cr, ct)
        integ = lambda y: WC(y)
        Sw = 2 * integrate(integ, (0, b/2))
        integ = lambda y: Cla(y) * WC(y)
        Claw = 2 / Sw * integrate(integ, (0, b/2))
        # Lift coefficient of 3D wing (elliptical wing formula)
        AR = b**2 / Sw
        if not ew: ew = 1 # Elliptical wing
        CLa = Claw / (1 + (57.296*Claw/pi/ew/AR))
        # Alfa zero Lift of wing
        integ = lambda y: (azl(y) - twist(y)) * WC(y)
        azL = 2 / Sw * integrate(integ, (0, b/2))
        # Moment coefficient (AC) of wing
        MAC = 2 / Sw * integrate(lambda y: WC(y)**2, (0, b/2))
        integ = lambda y: (Cm0(y) * WC(y)**2 -
                           pi * (azL - twist(y) - azl(y)) * WC(y) * xac(y))
        Cmacw = 2 / (Sw * MAC) * integrate(integ, (0, b/2))
        # Aerodynamic center
        integ = lambda y: Cla(y) * xac(y) * WC(y)
        xAC = (2 / Sw / CLa * integrate(integ, (0, b/2)))
        integ = lambda y: Cla(y) * y * WC(y)
        yAC = 2 / Sw / CLa * integrate(integ, (0, b/2))
        # Show results
        fields = [
            (u'Lift coefficient of 2D wing [1/deg]', 'text', u'%.4f' % Claw),
            (u'Lift coefficient of 3D wing [1/deg]', 'text', u'%.4f' % CLa),
            (u'Moment coefficient (AC) of wing [ ]', 'text', u'%.4f' % Cmacw),
            (u'Alfa zero Lift of wing [deg]', 'text', u'%.3f' % azL),
            (u'x of aero. center [%MAC]', 'text', u'%.2f' % xAC),
            (u'y of aero. center [%b]', 'text', u'%.2f' % yAC)]
        flag = appuifw.FFormDoubleSpaced
        form = appuifw.Form(fields, flag)
        form.execute()
    
def rotate_screen():
    """Rotate the screen and rebuilt the canvas"""
    global width, height, c, buffer
    screen = appuifw.app.orientation
    if screen == 'landscape':
        appuifw.app.orientation = 'portrait'
    else:
        appuifw.app.orientation = 'landscape'
    del c
    c = appuifw.Canvas(redraw_callback=draw)
    width, height = c.size
    buffer = buffer.resize(c.size)
        
def set_altitude(um):
    """Set altitude (um is unit of measurement)"""
    if um == 'm':
        altitude = appuifw.query(u'Insert altitude [m]:', 'float')
        tab_4(altitude)
    elif um == 'ft':
        altitude_ft = appuifw.query(u'Insert altitude [ft]:', 'float')
        altitude = altitude_ft * 0.3048
        tab_4(altitude)
    if altitude == None:
        appuifw.note(u'Altitude not set!', 'error')
        return

def quit():
    e32.Ao_lock().signal()

def tab_0():
    """Starting graphics"""
    buffer.clear()
    color = ((0, 0, 0),
             (0, 255, 0),
             (0, 150, 0))
    font = ((u'Nokia Hindi TitleSmBd S6', 30),
            (u'Nokia Hindi TitleSmBd S6', 15),
            (u'Nokia Hindi TitleSmBd S6', 15))
    text = (u'NACA PyFoil',
            u'By Ale152',
            u'www.wirgilio.it')
    box = (buffer.measure_text(text[0], font[0]),
           buffer.measure_text(text[1], font[1]),
           buffer.measure_text(text[2], font[2]))
    position = (((width-box[0][0][2])/2, 30),
                ((width-box[1][0][2])/2, 50),
                ((width-box[2][0][2])/2, 65))
    buffer.text(position[0], text[0], font=font[0], fill=color[0])
    buffer.text(position[1], text[1], font=font[1], fill=color[2])
    buffer.text(position[2], text[2], font=font[2], fill=color[2])
    s_width = width - 40 # Scaled width, for the border
    airfoil = [None, None, (0.12, 0, 0)] # NACA intro
    for x in xrange(s_width):
        x = float(x)/s_width
        # Airfoil (U: Upper, L: Lower)
        xL = xU = 20 + x * s_width
        yU = height/2 - thickness(x, airfoil) * s_width
        yL = height/2 + thickness(x, airfoil) * s_width
        buffer.line((xU, yU, xL, yL), outline=color[1], width=2)
        buffer.point((xL, yL), outline=color[2], width=2)
    draw()
        
def tab_1():
    """NACA Plot tab"""
    if not airfoil[0]:
        buffer.clear()
        position = (10, 30)
        color1 = (0, 0, 100) # Text
        color2 = (0, 0, 0) # Axes
        color3 = (80, 80, 80) # Units
        color4 = (0, 0, 200) # Function
        buffer.text(position, u'Please set a NACA from menu', fill=color1)
        # Draws an axes system (origin in [w/3, h/2])
        for k in range(30):
            xA = k * width/30
            yA = height/2
            xO = width/3
            yO = k * (height-50)/30
            buffer.line((xA, yA-2, xA, yA+3), outline=color3)
            buffer.line((xO-2, 50+yO, xO+3, 50+yO), outline=color3)
        buffer.line((width/3, 50, width/3, height), outline=color2)
        buffer.line((0, height/2, width, height/2), outline=color2)
        # Draws a function
        for t in xrange(900):
            t = float(t)
            x = t * cos(t*pi/180) / 15
            y = t * sin(t*pi/180) / 15
            buffer.point((width/3 + x, height/2 + y), outline=color4, width=2)
        draw()
        return
    else:
        NACA_plot()
        
def tab_2():
    """Dimensionless goup form"""
    buffer.clear()
    position = (10, 30)
    color1 = (0, 0, 100)
    color2 = (200, 0, 0)
    buffer.text(position, u'Select a group from menu', fill=color1)
    draw()

def tab_3():
    """Wings"""
    buffer.clear()
    position = (10, 30)
    color1 = (0, 0, 100)
    color2 = (200, 0, 0)
    buffer.text(position, u'Set wing parameter from menu', fill=color1)
    draw()
    
def tab_4(altitude=None):
    """Shows a form to calculate ISA parameters"""
    if altitude == None:
        buffer.clear()
        position = (10, 30)
        color = (0, 0, 100)
        buffer.text(position, u'Please set altitude from menu', fill=color)
        font = (u'Nokia Hindi TitleSmBd S6', 30)
        isa_text = [u'International', u'Standard', u'Atmosphere']
        buffer.text((10, 70), isa_text[0], font=font, fill=color)
        buffer.text((30, 100), isa_text[1], font=font, fill=color)
        buffer.text((50, 130), isa_text[2], font=font, fill=color)
        draw()
        return
    temp, press, dens = ISA(altitude)
    fields = [(u'Temperature [K]', 'text', u'%.3f' % temp),
              (u'Temperature [degC]', 'text', u'%.3f' % (temp - 273.15)),
              (u'Pressure [Pa]', 'text', u'%.3f' % press),
              (u'Density [kg/m^3]', 'text', u'%.3f' % dens)]
    flag = appuifw.FFormDoubleSpaced
    form = appuifw.Form(fields, flag)
    form.execute()

def set_tab(index):
    """Set tab function"""
    if index == 0: # Starting
        tab_0()
        appuifw.app.menu = menu_0
    elif index == 1: # NACA Plot
        tab_1()
        appuifw.app.menu = menu_1
    elif index == 2: # Dim.less goup
        tab_2()
        appuifw.app.menu = menu_2
    elif index == 3: # ISA
        tab_3()
        appuifw.app.menu = menu_3
    elif index == 4: # ISA
        tab_4()
        appuifw.app.menu = menu_4
        

tabs = [u'Intro', u'Plot', u'Group', u'Wing', u'ISA']
appuifw.app.set_tabs(tabs, set_tab)

# Starting menu
menu_0 = [(u'Rotate screen', rotate_screen),
          (u'About', lambda: appuifw.note(u'Created by Ale152', 'info')),
          (u'Quit', quit)]
# NACA Plot menu
menu_1 = [(u'Set NACA', NACA_set),
          (u'Plot', NACA_plot),
          (u'Export IMG', NACA_export),
          (u'Rotate screen', rotate_screen),
          (u'Quit', quit)]
# Dim.less group menu
menu_2 = [(u'Reynolds', Reynolds),
          (u'Mach', Mach),
          (u'Froude', Froude),
          (u'Rotate screen', rotate_screen),
          (u'Quit', quit)]
# Wings menu
menu_3 = [(u'Set wing geom', (
              (u'All', lambda: wing_set('geom', 'all')),
              (u'Wingspan', lambda: wing_set('geom', 'wingspan')),
              (u'Chords', lambda: wing_set('geom', 'chords')),
              (u'Sweep', lambda: wing_set('geom', 'sweep')),
              (u'Incidence', lambda: wing_set('geom', 'incid')),
              (u'Efficiency', lambda: wing_set('geom', 'effic')),
              (u'Twist', lambda: wing_set('geom', 'twist')))),
          (u'Set wing aero (root)', (
              (u'All', lambda: wing_set('aero_r', 'all')),
              (u'Copy from tip', lambda: wing_set('aero_r', 'copy')),
              (u'Alfa lift coeff.', lambda: wing_set('aero_r', 'Clar')),
              (u'Alfa moment coeff.', lambda: wing_set('aero_r', 'Cm0r')),
              (u'Alfa zero lift', lambda: wing_set('aero_r', 'azlr')),
              (u'Aero. center pos.', lambda: wing_set('aero_r', 'xacr')))),
          (u'Set wing aero (tip)', (
              (u'All', lambda: wing_set('aero_t', 'all')),
              (u'Copy from root', lambda: wing_set('aero_t', 'copy')),
              (u'Alfa lift coeff.', lambda: wing_set('aero_t', 'Clat')),
              (u'Alfa moment coeff.', lambda: wing_set('aero_t', 'Cm0t')),
              (u'Alfa zero lift', lambda: wing_set('aero_t', 'azlt')),
              (u'Aero. center pos.', lambda: wing_set('aero_t', 'xact')))),
          (u'Set horiz. tail', (
              (u'All', lambda: wing_set('aero_t', 'all')),
              (u'Copy from root', lambda: wing_set('aero_t', 'copy')),
              (u'Alfa lift coeff.', lambda: wing_set('aero_t', 'Clat')),
              (u'Alfa moment coeff.', lambda: wing_set('aero_t', 'Cm0t')),
              (u'Alfa zero lift', lambda: wing_set('aero_t', 'azlt')),
              (u'Aero. center pos.', lambda: wing_set('aero_t', 'xact')))),
          (u'Show geom info', lambda: wing_info('geom')),
          (u'Show aero info', lambda: wing_info('aero')),
          (u'Draw wing', wing_draw),
          (u'Rotate screen', rotate_screen),
          (u'Quit', quit)]
# ISA menu
menu_4 = [(u'Set altitude', (
              (u'Meters', lambda: set_altitude('m')),
              (u'Feet', lambda: set_altitude('ft')))),
          (u'Rotate screen', rotate_screen),
          (u'Quit', quit)]

set_tab(0)
app_lock = e32.Ao_lock()
app_lock.wait()