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

ciao,
che telefono utilizzi per far girare questo programma ?
Io devo cambiare cellulare ( attualmente ho un nokia 6630 ) e stavo pensando di prendermi un Nokia c5 o un 5230.
edito il messaggio di prima : intendevo un 5530. comunque bell’applicazione ! appena posso la installo !
@Newengine:
.
.
Grazie
Per far girare le applicazioni in Python utilizzo diversi cellulari Symbian (tra cui anche il 6630!), che è un sistema per il quale si trovano applicazioni di ogni tipo.
Per quanto riguarda la tua scelta, ti consiglio sicuramente il 5530. Sia perché ha il Wi-Fi, che è una tecnologia attualmente indispensabile, sia perché ha un display molto più grande che è touchscreen.
Ad ogni modo, se hai qualcosina da spendere in più, ti consiglio il Nokia N900 che monta Maemo, una piattaforma derivata da Linux
eh mi piacerebbe comprare un n900 ma non ho abbastanza soldi
posso chiederti come fare per installare questo programmino per 6630 ?
so che prima devo installare python e ho anche provato a scaricare python for s60 . sys ma quando lo vado ad aprire mi dice ” file danneggiato “.
@Newengine:
Il Nokia 6630 è molto vecchio e purtroppo non è più supportato dalle nuove versioni di Python.
La mia applicazione dovrebbe funzionare comunque, dato che non fa utilizzo di moduli esterni e infatti fino a qualche versione fa funzionava, ma questa versione non l’ho ancora testata.
Puoi scaricare Python per il 6630 a questo indirizzo:
Python per 6630
La shell la puoi trovare a quest’altro indirizzo:
Python Shell per 6630
Installa prima Python e poi la Shell, poi copia il codice del programma in un file rinominato .py e invialo tramite Bluetooth al cellulare, oppure copialo direttamente nella cartella C:\python della memoria del cellulare (E: per la memory card).
Infine avvia la shell dal menù applicazioni ed esegui lo script.
grazie 1000 ora funziona tutto perfettamente !!! utilissimo ! mannaggia che non sono bravo in python se no ti avrei volentieri dato una mano a risolvere i bug
@Newengine:
Non preoccuparti, appena ho un po’ di tempo cerco di risolvere i bug e completo l’applicazione