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()