In [20]:
using Plots
using DataFrames
using LinearAlgebra


# Mass and CG of existing system¶

## From flight manual¶

• Basic empty weight: 175kg
• The C.G. position of an empty motorglider (airframe + engine):
• with the engine in: 550mm
• with the engine out: 605mm
• Diameter of the propeller is 1180mm.

We must guess at where the engine pylon is located. We do this from the profile view in the manual, and arrive at 5300/375*93 = ~1300mm from the datum (i.e. the leading edge)

In [15]:
emptyMass = 175
emptyCg_engineIn = .550
emptyCg_engineOut = .605

deltaCg = emptyCg_engineOut - emptyCg_engineIn

propellerEngineOffset = .1

# Assume that the pylon length is the propeller radius, minus the a bit because the engine is lower than the propeller
pylonLength = 1.180/2 - propellerEngineOffset

# CG + deltaCG = sum(Moments + deltaEngineMoment)/sum(Masses) --> deltaEngineMoment = deltaCG * Mass
deltaEngineMoment = deltaCg * emptyMass

# deltaEngineMoment = engineMass * arm_out - engineMass * arm_in = motorMass * (arm_out - arm_in) = engineMass * ((arm_in + pylonLength) - arm_in) = engineMass*(pylonLength) --> engineMass = deltaEngineMoment / pylonLength
engineMass = round(deltaEngineMoment / pylonLength, digits=2)

Out[15]:
19.64

# Mass and CG of new system¶

In [16]:
# All weight in kg
# All distances in m

w_empty = 175
L_empty = .550

w_pilot = 85
L_pilot = -.380

w_engine = -engineMass
L_engineOut = 1.300
L_engineIn = 1.300 - pylonLength

# Assume the propeller hub, blades, and spinner are part of the same package
w_woodPropeller = -1.6
L_woodPropellerIn = L_engineIn
L_woodPropellerOut = L_engineOut + .1  # This allows the propeller to be shifted a few cm behind the motor. Note that this value is only applicable when the engine is extended

w_fuelTank = -(1 + 3)  # 1kg for the tank, and 3kg of fuel
L_fuelTank = 0  # It's basically right above the CG

# Assume using the REG 30 from Rotex
w_motor = 4.1
L_motorIn = L_engineIn + propellerEngineOffset
L_motorOut = L_engineOut - .05  # This allows the motor to be shifted a few cm away from the pylon. Note that this value is only applicable when the engine is extended

w_motorMount = 0.3  # Guesstimate about firewall weight, with hardware
L_motorMountIn = L_motorIn
L_motorMountOut = L_motorOut

# Assume the propeller hub, blades, and spinner are part of the same package
w_CfPropeller = 0.8
L_CfPropellerIn = L_motorIn
L_CfPropellerOut = L_motorOut + .1  # This allows the propeller to be shifted a few cm behind the motor. Note that this value is only applicable when the engine is extended

# Guessing a screen + throttle lever will weigh around 250g
w_controlPanel = 0.25
L_controlPanel = -0.890

# Removing the SLA
w_controlPanel = 6
L_controlPanel = -0.890

# Assume the battery pack will be ~30cm long, and that it can't be coser than a few inches from the rear bulkhead
w_battery = 13
L_battery = (L_engineOut - .3/2 - 0.30)

# Guessing a quality ESC with big heat sinks will weigh 1.5kg
w_ESC = 1.5 * 2.2
L_ESC = L_battery + .10

# Assume 4AWG aluminum wire (C.f. http://www.interfacebus.com/Aluminum_Wire_AWG_Size.html) and allow for 50% insulation weight
cableWeightPerFoot_lbs = 39.2/1000*1.5
cableResistancePerFoot = 0.408/1000

L_cables = (L_motorIn - L_battery)/2 # assume that the cable's CG is at the midway point
w_cables = 2* (L_cables*2/.0254)/12 * cableWeightPerFoot_lbs /2.2  # Don't forget to multiply by 2 for both (+) and (-) cables and by 2 for doubling the length from the CG

# Create an empty dataframe
df = DataFrame(Name = String[], Weight_kg = Float64[],
MomentArm_In = Float64[], MomentArm_Out = Float64[])

# Populate it
push!(df, ["empty airframe", w_empty, L_empty, L_empty] )
push!(df, ["removed engine", w_engine, L_engineIn, L_engineIn] )
push!(df, ["removed wood propeller", w_woodPropeller, L_woodPropellerIn, L_woodPropellerIn] )
push!(df, ["pilot + parachute", w_pilot, L_pilot, L_pilot] )
push!(df, ["battery", w_battery, L_battery, L_battery] )
push!(df, ["cables", w_cables, L_cables, L_cables] )
push!(df, ["motor mount", w_motorMount, L_motorMountIn, L_motorMountOut] )
push!(df, ["control panel", w_controlPanel, L_controlPanel, L_controlPanel] )
push!(df, ["motor", w_motor, L_motorIn, L_motorOut] )
push!(df, ["CF propeller", w_CfPropeller, L_CfPropellerIn, L_CfPropellerOut] )
push!(df, ["ESC", w_ESC, L_ESC, L_ESC] )

display(df)
println()

# Calculate the CG
weights = df[!,:Weight_kg]
momentArmsIn =  df[!,:MomentArm_In]
momentArmsOut =  df[!,:MomentArm_Out]

netMomentIn = weights' * momentArmsIn
cgIn = netMomentIn / sum(weights)
cgIn_stationPercentage = ((cgIn - 0.127)/(0.267 - 0.127)) * 100

netMomentOut = weights' * momentArmsOut
cgOut = netMomentOut / sum(weights)
cgOut_stationPercentage = ((cgOut - 0.127)/(0.267 - 0.127)) * 100

println("Weight: ", round(sum(weights), digits=1), "kg, ", round(sum(weights)/300 * 100), "% of MTOW (300kg)")
println("min CG: 127mm, max CG: 267mm")

println("CG motor retracted: ", round(cgIn*1000), "mm, ", round(cgIn_stationPercentage), "%")

println("CG motor deployed: ", round(cgOut*1000), "mm, ", round(cgOut_stationPercentage), "%")


11 rows × 4 columns

NameWeight_kgMomentArm_InMomentArm_Out
StringFloat64Float64Float64
1empty airframe175.00.550.55
2removed engine-19.640.810.81
3removed wood propeller-1.60.810.81
4pilot + parachute85.0-0.38-0.38
5battery13.00.850.85
6cables0.01052250.030.03
7motor mount0.30.911.25
8control panel6.0-0.89-0.89
9motor4.10.911.25
10CF propeller0.80.911.35
11ESC3.30.950.95
Weight: 266.3kg, 89.0% of MTOW (300kg)
min CG: 127mm, max CG: 267mm
CG motor retracted: 227.0mm, 71.0%
CG motor deployed: 233.0mm, 76.0%


• Gas springs for lowering the engine are now too strong?
• The motor is now easier to raise from the cockpit, no longer requiring as much muscle.
• The ESC can stop the motor in the appropriate position, or have a very slow "cogging mode" which advances the motor until the propeller reaches the stop.
• Reduced prop breakage because the stop pin no longer accidentally pierces the prop?

# Airframe power requirements¶

In [17]:
MTOW = 300  # Maximum takeoff weight, in [kg].
ld_deployed = 18 # L/D when engine is deployed
sinkRate_best = 0.82 * 1.1# in [m/s], downrated ~10% from published figures

v_ld = 53 *.511  # in [m/s]
v_sr = 48 *.511  # in [m/s]. This is a guesstimate from the stall at 36kts and the best LD at 53kts.

# Desired climb rate, in [m/s]
vz_desired = 2.5

# Calculate thrust required for level flight
F_cruise = MTOW*9.81/ld_deployed

# Calculate cruise and climb mechanical power
P_cruise = F_cruise*v_ld
P_climb  = F_cruise*v_sr + MTOW*9.81 * vz_desired

F_climb  = P_climb/v_sr

println(round.([F_cruise F_climb P_cruise P_climb], digits = 1))

[163.5 463.5 4428.1 11367.8]


## Powerplant description¶

In [18]:
# Propeller max efficiency, in %/100
eta_prop_climb = .7
eta_prop_cruise = .75

# Transmission effiency, in %/100
eta_transmission = .98

# Calculate shaft power
P_shaft_cruise = P_cruise / (eta_prop_cruise * eta_transmission)
P_shaft_max = P_climb / (eta_prop_climb * eta_transmission)

# Motor efficiency, in %/100
eta_motor_cruisePower = .93
eta_motor_maxPower = .90

# Calculate motor power
P_motor_cruise = P_shaft_cruise / eta_motor_cruisePower
P_motor_max = P_shaft_max / eta_motor_maxPower

# Calculate motor power loss
P_motorLosses_cruise = P_motor_cruise * (1-eta_motor_cruisePower)
P_motorLosses_max = P_motor_max * (1-eta_motor_maxPower)

println("Shaft power out: ", round.([P_shaft_cruise P_shaft_max], digits = 1))

println("Motor power in: ", round.([P_motor_cruise P_motor_max P_motorLosses_max P_motorLosses_cruise], digits = 1))

Shaft power out: [6024.6 16571.2]
Motor power in: [6478.0 18412.4 1841.2 453.5]


# Costs¶

In [19]:
euro2dollar = 1.09

# Create an empty dataframe
df = DataFrame(Item = String[], Price = Float64[])

# Prices in \$, not including shipping, taxes, or duties
p_motor = 1400 * euro2dollar
p_propeller = 300 * euro2dollar
p_battery = (420*6) * .9  # Represents the 10% discount for bulk orders
p_ESC = 979 * euro2dollar
p_cables = 40
p_hardware = 100
p_controlPanel = 200
p_Charger = 270

# Populate it
push!(df, ["battery", p_battery] )
push!(df, ["cables", p_cables] )
push!(df, ["hardware", p_hardware] )
push!(df, ["control panel", p_controlPanel] )
push!(df, ["motor", p_motor] )
push!(df, ["propeller", p_propeller] )
push!(df, ["ESC", p_ESC] )
push!(df, ["charger", p_Charger] )

display(df)
println()

costs = df[!,:Price]

subTotal = sum(costs)

# Add in 15% for shipping and other costs
totalPrice = subTotal * 1.15

println(round.([subTotal totalPrice]))


8 rows × 2 columns

ItemPrice
StringFloat64
1battery2268.0
2cables40.0
3hardware100.0
4control panel200.0
5motor1526.0
6propeller327.0
7ESC1067.11
8charger270.0
[5798.0 6668.0]

In [ ]:


In [ ]: