Virtual flight is a homepage which introduces FlightGear Flight Simulator.

積荷と重量



   

●ドロップタンクの装着-燃料の設定

【コメント】

 ドロップタンクにしろ武装にしろ、これらを搭載すれば機体の重量やバランスに影響を及ぼします。リアルフライトシミュレーターを謳うFlightGearではどう扱われているのでしょうか? とりわけ、ドロップタンクは敵と遭遇した戦闘機がこれを切り離して身軽になって空中戦に臨む場合があるように、途中燃料を残したままこれを切り離した場合は、重量の変化はもとよりその後の飛行距離に影響が及ぶ以上、きちんと再現がのぞまれます。

 FlightGearで遊ぶようになり、最初に行った改造がドロップタンクの装着でした。以来、課題となっていたこの問題に、まじにとりくんでみた結果をメモに残したのがこの項の内容です。

 ウィンドウ上部のメニューバーの[Equipment]から[Fuel And Payload]を経て開くことのできるWeight and Fuel Settingの画面の存在は、武装の選択にこれを利用している機体(A-10)があったことから知ってはいましたが、これまでさわってみたことがありません。
 今回、ドロップタンクではおなじみのSeahawkを起動し、この画面を開き、スライドバーで内部タンクの燃料の量をいじっていたら、バーがひとりでに動いて増槽から勢いよく燃料が流れ込んで行くではありませんか。またドロップタンクを切り離せば、増槽の表示はゼロに。これが使えたら、なんのことない、問題は解決です。
 この設定画面は、自作した機体であるJu EF128(注1参照)にもできています。キャパシティにはフライトモデルに記述された量が指定され、実際の量にはjuef128-set.xmlに記述の量のところにバーがきています。武装の選択に使っている機体があるということは、この画面は編集が可能ということであり、まずはこれを調べてみる価値が十分ありそうです。

 少なくとも、燃料タンクの設定はjuef128-set.xmlでOK!であることがわかりました。Seahawkを見習って記述。他方、増槽から内部タンクへの燃料の動きは、やはりNASALが司っていました。こちらは、Seahawkのfuel-coks.nasをまるごとコピーし、またseahawk.nas中の燃料とドロップタンクにかかわる部分を抜き書きしてfuel.nasというファイルにしました。ややこしくなるので、記述はさわらずもとのままにしました。nasファイルが使えるように、juef128.xmlに記述を加えておくことはもちろんです。

 なお、ドロップタンクの燃料消費、タンクの切り離しに伴う機体の重量変化については、不確かなままにしておけないので、我が愛機Ju EF128、厳密には、増槽などを付けてもよい機体はjuef128_customとしていますので、こちらで確認をおこないました。
 juef128_custom-set.xml中の<consumables>タグ内に記述されたドロップタンクの燃料に関する<level-gal_us>の値をゼロにしたり、大きな数値にしたりしてみたところ、明らかにドロップタンクの切り離しで機体が軽くなり浮き上がること、離陸の際には、ドロップタンクを切り離すと急角度で上昇できるようになることが見てとれました。

注1)3Dモデルやフライトモデルを含め、自分でまがりなりにも一からつくったはじめての機体がJunkers Ju Ef128です。詳しくは自前の飛行機作りに挑戦のページをご覧ください。機体のファイルはこちらからダウンロードできます。


droptank

↑Weight and Fuel Settingの画面
増槽を切り離すと、橙色のバーのうち増槽を示す下2つがゼロに


【実際】

〔Aircraft/juef128/juef128_custom.xml(フライトモデル)中の記述〕

   (略)
 <propulsion>
   (略)
  <tank type="FUEL" number="0">
   <location unit="M">
    <x> -0.70 </x>
    <y> 0.00 </y>
    <z> 0.04 </z>
   </location>
   <capacity unit="LBS"> 2755.00 </capacity>
   <contents unit="LBS"> 1500.00 </contents>
  </tank>

  <tank type="FUEL" number="1">
   <location unit="M">
    <x> -0.00 </x>
    <y> 0.00 </y>
    <z> 0.04 </z>
   </location>
   <!--<capacity unit="LBS"> 0.00 </capacity>
   <contents unit="LBS"> 0.00 </contents>-->
   <capacity unit="LBS"> 100.00 </capacity>
   <contents unit="LBS"> 50.00 </contents>
  </tank>

  <tank type="FUEL" number="2">
   <location unit="M">
    <x> -0.30 </x>
    <y> -1.75 </y>
    <z> -0.0715 </z>
   </location>
   <capacity unit="LBS"> 3000.00 </capacity>
   <contents unit="LBS"> 1500.00 </contents>
  </tank>

  <tank type="FUEL" number="3">
   <location unit="M">
    <x> -0.30 </x>
    <y> 1.75 </y>
    <z> -0.0715 </z>
   </location>
   <capacity unit="LBS"> 3000.00 </capacity>
   <contents unit="LBS"> 1500.00 </contents>
  </tank>

 </propulsion>
   (略)


〔Aircraft/juef128/juef128_custom-set.xml中の記述〕

   (略)
<!-- fuel: + drop tank -->
    <consumables>
        <fuel>
            <tank n="0">
                <name type="string">inner</name>
                <level-gal_us>414</level-gal_us>
                <selected type="bool">1</selected>
            </tank>
            <tank n="1">
                <name type="string"></name>
                <level-gal_us>0</level-gal_us>
                <selected type="bool">0</selected>
            </tank>
            <tank n="2">
                <name type="string">Droptank Stbd</name>
                <level-gal_us>240</level-gal_us>
                <selected type="bool">0</selected>
            </tank>
            <tank n="3">
                <name type="string">Droptank Port</name>
                <level-gal_us>240</level-gal_us>
                <selected type="bool">0</selected>
            </tank>
        </fuel>
    </consumables>
   (略)


〔fuel.nasの記述〕

# ==================================== timer stuff ===============================

# set the update period

UPDATE_PERIOD = 0.3;

# set the timer for the selected function

var registerTimer = func {

    settimer(arg[0], UPDATE_PERIOD);

} # end function

# ======================================= fuel tank stuff ===================================

# operate fuel cocks

var openCock=func{

    var cock=getprop("controls/engines/engine/fuel-cock/lever");

    if (cock < 1){
        cock = cock +1;
        setprop("controls/engines/engine/fuel-cock/lever",cock);
        adjustCock()
    }

}#end func

var closeCock=func{

    var cock = getprop("controls/engines/engine/fuel-cock/lever");

    if (cock > 0){
        cock = cock - 1;
        setprop("controls/engines/engine/fuel-cock/lever",cock);
        adjustCock()
    }

}#end func


# adjust fuel cocks

var adjustCock = func{

    var lever=getprop("controls/engines/engine/fuel-cock/lever");

    if (lever == 0){
        setprop("consumables/fuel/tank[0]/selected",0);
        setprop("consumables/fuel/tank[1]/selected",0);
        setprop("consumables/fuel/tank[2]/selected",0);
    }
    else{
        setprop("consumables/fuel/tank[0]/selected",1);
        setprop("consumables/fuel/tank[1]/selected",1);
        setprop("consumables/fuel/tank[2]/selected",0);
    }


}#end func

# tranfer fuel

fuelTrans = func {

    var amount = 0;
    var maxFlowRate = 1;

    if(getprop("/sim/freeze/fuel")) { return registerTimer(fuelTrans); }

    capacityFwd = getprop("consumables/fuel/tank[0]/capacity-gal_us");
    if(capacityFwd == nil) { capacityFwd = 0; }

    var levelFwd = getprop("consumables/fuel/tank[0]/level-gal_us");
    if(levelFwd == nil) { levelFwd = 0; }

    var levelSaddle = getprop("consumables/fuel/tank[2]/level-gal_us");
    if(levelSaddle == nil) { levelSaddle = 0; }

    var levelDropStbd = getprop("consumables/fuel/tank[3]/level-gal_us");
    if(levelDropStbd == nil) { levelDropStbd = 0; }

    var levelDropPort = getprop("consumables/fuel/tank[4]/level-gal_us");
    if(levelDropPort == nil) { levelDropPort = 0; }

    if ( capacityFwd > levelFwd and levelDropStbd > 0){

        amount = capacityFwd - levelFwd;
        if (amount > levelDropStbd) {
            amount = levelDropStbd;
        }
        if (amount > maxFlowRate) {
            amount = maxFlowRate;
        }
        levelDropStbd = levelDropStbd - amount/2;
        levelDropPort = levelDropPort - amount/2;
        levelFwd = levelFwd + amount;
        setprop( "consumables/fuel/tank[3]/level-gal_us",levelDropStbd);
        setprop( "consumables/fuel/tank[4]/level-gal_us",levelDropPort);
        setprop( "consumables/fuel/tank[0]/level-gal_us",levelFwd);
    }

    if ( capacityFwd > levelFwd and levelSaddle > 0){
        amount = capacityFwd - levelFwd;
        if (amount > levelSaddle) {
            amount = levelSaddle;
        }
        if (amount > maxFlowRate) {
            amount = maxFlowRate;
        }
        levelSaddle = levelSaddle - amount;
        levelFwd = levelFwd + amount;         setprop( "consumables/fuel/tank[2]/level-gal_us",levelSaddle);
        setprop( "consumables/fuel/tank[0]/level-gal_us",levelFwd);
    }

#print("Upper: ",levelSaddle, " Lower: ",levelFwd);
#print( " Amount: ",amount);

    registerTimer(fuelTrans);

} # end funtion fuelTrans

# fire it up

registerTimer(fuelTrans);

# ========================== end fuel stuff ======================================

#================================== Droptanks ================================
print("droptanks starting");
var droptank_node = props.globals.getNode("sim/ai/aircraft/warfare/droptank", 1);
var ext_force_stbd_node = props.globals.getNode("sim/ai/ballistic/force", 1);
ext_force_stbd_node.getChild("force-lb", 0, 1).setDoubleValue(0);
ext_force_stbd_node.getChild("force-azimuth-deg", 0, 1).setDoubleValue(0);
ext_force_stbd_node.getChild("force-elevation-deg", 0, 1).setDoubleValue(0);
ext_force_stbd_node.getChild("force-norm", 0, 1).setDoubleValue(0);

var ext_force_port_node = props.globals.getNode("sim/ai/ballistic/force[1]", 1);
ext_force_port_node.getChild("force-lb", 0, 1).setDoubleValue(0);
ext_force_port_node.getChild("force-azimuth-deg", 0, 1).setDoubleValue(0);
ext_force_port_node.getChild("force-elevation-deg", 0, 1).setDoubleValue(0);

var ext_force_extra_node = props.globals.getNode("sim/ai/ballistic/force[2]", 1);
ext_force_extra_node.getChild("force-lb", 0, 1).setDoubleValue(0);
ext_force_extra_node.getChild("force-azimuth-deg", 0, 1).setDoubleValue(0);
ext_force_extra_node.getChild("force-elevation-deg", 0, 1).setDoubleValue(90);

var pitch_node = props.globals.getNode("orientation/pitch-deg", 1);
var hdg_node = props.globals.getNode("orientation/heading-deg", 1);

var droptanks = func(n) {
    var droptank = droptank_node.getValue();
    var node = props.globals.getNode(n.getValue(), 1);
    print (" droptank ", droptank, " lon " , node.getNode("impact/longitude-deg").getValue(),);
    geo.put_model("Aircraft/juef128/Models/droptank-hot.xml",
        node.getNode("impact/latitude-deg").getValue(),
        node.getNode("impact/longitude-deg").getValue(),
        node.getNode("impact/elevation-m").getValue()+ 0.25,
        node.getNode("impact/heading-deg").getValue(),
        0,
        0
        );
}

setlistener("sim/ai/aircraft/impact/droptank", droptanks);

var ext_force_stbd = func {
  if(ext_force_stbd_node.getChild("force-lb", 0, 1).getValue() != 0){
    ext_force_stbd_node.getChild("force-lb", 0, 1).setDoubleValue(0);
    ext_force_stbd_node.getChild("force-norm", 0, 1).setDoubleValue(0);
    return;
  } else {
    ext_force_stbd_node.getChild("force-lb", 0, 1).setDoubleValue(1000);
    ext_force_stbd_node.getChild("force-azimuth-deg", 0, 1).setDoubleValue(hdg_node.getValue());
    ext_force_stbd_node.getChild("force-elevation-deg", 0, 1).setDoubleValue(pitch_node.getValue()-90);
    ext_force_stbd_node.getChild("force-norm", 0, 1).setDoubleValue(1);
    setprop("ai/models/ballistic[1]/controls/slave-to-ac",0);
    setprop("ai/models/ballistic[3]/controls/slave-to-ac",0);
    settimer(ext_force_stbd,0.75);
  }
}

setlistener( "controls/armament/station[1]/jettison-all", ext_force_stbd);

var ext_force_port = func {
  if(ext_force_port_node.getChild("force-lb", 0, 1).getValue() != 0){
    ext_force_port_node.getChild("force-lb", 0, 1).setDoubleValue(0);
    ext_force_port_node.getChild("force-norm", 0, 1).setDoubleValue(0);
    return;
  } else {
    ext_force_port_node.getChild("force-norm", 0, 1).setDoubleValue(1);
    ext_force_port_node.getChild("force-lb", 0, 1).setDoubleValue(1000);
    ext_force_port_node.getChild("force-azimuth-deg", 0, 1).setDoubleValue(hdg_node.getValue());
    ext_force_port_node.getChild("force-elevation-deg", 0, 1).setDoubleValue(pitch_node.getValue()-90);
    ext_force_port_node.getChild("force-norm", 0, 1).setDoubleValue(1);
# print ("elevation ", ext_force_port_node.getChild("force-elevation-deg", 0, 1).getValue());
    setprop("ai/models/ballistic[0]/controls/slave-to-ac",0);
    setprop("ai/models/ballistic[2]/controls/slave-to-ac",0);
    settimer(ext_force_port,0.75);
  }
}

setlistener( "controls/armament/station[0]/jettison-all", ext_force_port);

print("droptanks running");

setlistener("/sim/signals/fdm-initialized", func {
  var droptank_set_node = props.globals.getNode("sim/stores/load-tanks", 1);
  droptank_set_node.setBoolValue(1);
  var droptank_set1_node = props.globals.getNode("sim/stores/load-tanks[1]", 1);
  droptank_set1_node.setBoolValue(1);
  print ("loading droptanks");
    }
);
# end
fuel-cock.nasの記述
# Properties under /consumables/fuel/tank[n]:
# + level-gal_us - Current fuel load. Can be set by user code.
# + level-lbs - OUTPUT ONLY property, do not try to set
# + selected - boolean indicating tank selection.
# + density-ppg - Fuel density, in lbs/gallon.
# + capacity-gal_us - Tank capacity
#
# Properties under /engines/engine[n]:
# + fuel-consumed-lbs - Output from the FDM, zeroed by this script
# + out-of-fuel - boolean, set by this code.


var UPDATE_PERIOD = 0.3;


var update = func {
    if (fuel_freeze) {
        return;
    }

    var consumed_fuel = 0;
    foreach (var e;
engines) {
        var fuel = e.getNode("fuel-consumed-lbs");
        consumed_fuel += fuel.getValue();
        fuel.setDoubleValue(0);
    }

    if (!consumed_fuel) {
        return;

    }

    var selected_tanks = [];
    foreach (var t;
tanks) {
        var cap = t.getNode("capacity-gal_us").getValue();
        if (cap > 0.01 and t.getNode("selected").getBoolValue()) {
            append(selected_tanks, t);
        }
    }

    # Subtract fuel from tanks, set auxilliary properties. Set out-of-fuel
    # when any all connected tanks are dry.
    var out_of_fuel = 0;
    if (size(selected_tanks) == 0) {
    out_of_fuel = 1;
    } else {
        var fuel_per_tank = consumed_fuel / size(selected_tanks);
        foreach (var t;
selected_tanks) {
            var ppg = t.getNode("density-ppg").getValue();
            var lbs = t.getNode("level-gal_us").getValue() * ppg;
            lbs = lbs - fuel_per_tank;
            if (lbs < 0) {
            lbs = 0;
                # Kill the engines if we're told to, otherwise simply
                # deselect the tank.
                if (t.getNode("kill-when-empty", 1).getBoolValue()) {
                    out_of_fuel = 1;
                } else {
                t.getNode("selected").setBoolValue(0);
                }
            }
            var gals = lbs / ppg;
            t.getNode("level-gal_us").setDoubleValue(gals);
            t.getNode("level-lbs").setDoubleValue(lbs);
        }
    }

    # Total fuel properties
    var lbs = 0;
    var gals = 0;
    var cap = 0;

    foreach (var t;
tanks) {
        var level = t.getNode("level-gal_us").getValue();
        var density = t.getNode("density-ppg").getValue();
        t.getNode("level-lbs").setDoubleValue(level * density);
# print ("level " , level, " density " , density, " lbs ", level * density);
        lbs += t.getNode("level-lbs").getValue();
        gals += level;
        cap += t.getNode("capacity-gal_us").getValue();
    }

    total_lbs.setDoubleValue(lbs);
    total_gals.setDoubleValue(gals);
    total_norm.setDoubleValue(gals / cap);
    foreach (var e;
engines) {
        e.getNode("out-of-fuel").setBoolValue(out_of_fuel);
    }
}

var loop = func {
    update();
    settimer(loop, UPDATE_PERIOD);
}

var init_double_prop = func(node, prop, val) {
    if (node.getNode(prop) != nil) {
        val = num(node.getNode(prop).getValue());
    }
    node.getNode(prop, 1).setDoubleValue(val);

    foreach (var t;
tanks) {
        var level = t.getNode("level-gal_us").getValue();
        var density = t.getNode("density-ppg").getValue();
        t.getNode("level-lbs").setDoubleValue(level * density);
# print (t , " level " , level, " density " , density, " lbs ", level * density);
#lbs += t.getNode("level-lbs").getValue();
# gals += level;
# cap += t.getNode("capacity-gal_us").getValue();
    }
}

var tanks = [];
var engines = [];
var fuel_freeze = nil;
var total_gals = nil;
var total_lbs = nil;
var total_norm = nil;


var L = _setlistener("/sim/signals/fdm-initialized", func {
    removelistener(L);

    setlistener("/sim/freeze/fuel", func(n) { fuel_freeze = n.getBoolValue() }, 1);

    total_gals = props.globals.getNode("/consumables/fuel/total-fuel-gals", 1);
    total_lbs = props.globals.getNode("/consumables/fuel/total-fuel-lbs", 1);
    total_norm = props.globals.getNode("/consumables/fuel/total-fuel-norm", 1);

    engines = props.globals.getNode("engines", 1).getChildren("engine");
    foreach (var e;
engines) {
        e.getNode("fuel-consumed-lbs", 1).setDoubleValue(0);
        e.getNode("out-of-fuel", 1).setBoolValue(0);
    }

    foreach (var t;
props.globals.getNode("/consumables/fuel", 1).getChildren("tank")) {
        if (!size(t.getChildren())) {
            continue;
# skip native_fdm.cxx generated zombie tanks
        }
        append(tanks, t);
        init_double_prop(t, "level-gal_us", 0.0);
        init_double_prop(t, "level-lbs", 0.0);
        init_double_prop(t, "capacity-gal_us", 0.01);
# not zero (div/zero issue)
        init_double_prop(t, "density-ppg", 6.0);
# gasoline

        if (t.getNode("selected") == nil) {
            t.getNode("selected", 1).setBoolValue(1);
        }
    }

    loop();
});


[2012/07/22][2012/12/01更新] [2018/04/23刷新]


   

●Weight and FuelSettingsのプログラミング-Yasimの場合

【コメント】

 ドロップタンクについては、その脱着が機体に及ぼす重量変化をWeight and Fuel Settingsにプログラミングすることで再現できることは、前述のとおりです。

 それならば、各種武装にあたっても、重量の重い爆弾もあるのだから、その重量変化を反映できるようにしなければ一貫しないというもの。FlightGearではどうしているのでしょう?

 FlightGearでは様々な武器を搭載するのに、いくつかの機体でみかけるのがやはりWeight and Fuel Settingsを用いた方法です。他には少数、Select Liveryを用いた例もあるようですが、ここではせっかくFlightGearのメニューバーにそなわった「Fuel and Payload」から開くWeight and Fuel Settingsがあるのですから、これで各種武器を搭載できるようにするには、どのようにプログラムを記述すればよいものか、調べてみることにしました。

 はじめは、我がJunkers Ju EF128でWeight and Fuel Settingsを用いた増槽、武器の搭載を目論んだのですが、どうもフライトモデルがYasimでないと動かないようです。JSBSimをフライトモデルとするJu EF128で実現する方法がわからず、あえなく挫折。Yasimでフライトモデルが書けるようにならないといけないのかと、しばし茫然自失。

 気を取り直し、Yasimならほんとうに機能するのか試すため、Messerschmitt LibelleにHarrierのシステムを一括移植してみることにし」ました。

 試行錯誤の上、ようやく武装をセットできるようになりましたが、燃料タンクには振り回されました。
 ひとつは、harrierとLibelleでタンクの数が違い、一部を除き搭載武器の重量が反映されなかったこと。気づくのに1日を必要としました。
 さらにひどかったのは、Weight and Fuel Settings自体は設定画面なのですから、ドロップタンクの搭載を選択しても、燃料をどれくらい入れるのか、自分で指定してやらないといけないのに気がつかず、ちっとも反映しないと悩んだこと。
 元の機体でも反映していないことから、ようやくチェックボックスにチェックを入れ、バーをスライドさせればよいことに気付きましたが、これには数日を要してしまいました。

 わかってしまえば簡単なことなのに、は、は、は。FlightGearを楽しむ目的が、飛行機を飛ばすことから改造することになってしまっていた報いですね。

 さて、Harrierは多数の武器を搭載できますが、その使用、発射や投下がプログラミングされていないので、厳密に例えば爆弾投下前後での重量変化が反映されているかが確認できません。またMesserschmitt Libelleでは胴体と地面の間に充分なスペースがなく、爆弾や増槽を吊り下げることは難しそう。そこで、同じWeight and Fuel Settingsを用いるものの武装はシンプルで、かつ使用もできる FW-190からMe-262への移植を行いました(注1参照)。
 なお、複数の武器を選択搭載できるFW-190ですが、武器は<weight n="x">タグ中に<opt>タグをもうけ、名称と重さを記入することで登録され選択が可能になります。ただし、装着の条件はその重さになっているようで、実際に武装を増やしX-4ミサイルを搭載することにし、X-4の重量をWGr 21ロケット弾と同じにすると、X-4とWGr 21が一緒に出現してしまいました。

注1)FW-190での武器の使用
FW-190はHelpに書かれた方法では、武器の使用ができません。
というのもHelpにはToggle Master Arm のon/offがm/Mキーと記されているのにもかかわらず、実際にはプログラムされているのがd/Dキーとなっているから。

drop

↑WGr 21ロケット弾や爆弾を搭載できるようにしたMe262の、Weight and Fuel Settings画面
左側が燃料タンク、右側がペイロードの設定


[2012/07/22][2012/12/01更新][2018/04/23刷新]


   

●ステーション選択と荷重-JSBSimの場合

【コメント】

 いくつかの米軍機について調べた際に、ベトナム戦争で使用されたセンチュリーシリーズの米軍機で、David Culp氏の手になるFlightGear用の機体をいくつか触ってみる機会がありました。

 そのなかでF-100Dスーパーセイバーが面白い動きをするのに気がつきました。翼に吊り下げられた爆弾を投下すると、投下した爆弾があった方の翼が浮き上がり機体が傾くのです。反対側の翼の爆弾を投下すると、今度はそちら側が浮き上がって傾く。重たい爆弾が切り離されたら、軽くなることをリアルに再現しているわけです。

 F-105サンダーチーフでは、両翼の爆弾が左右同時に投下されるので、傾きなどいわば再現する必要もないのですが、F-100Dは投下する爆弾をスイッチでひとつひとつ選択できるようになっていたことから、爆弾の重さが機体に及ぼす影響がリアルに再現されていることがわかったもの。

 現実世界での空爆で爆弾の投下が、1個1個行われるものなのか、それとも左右同時に投下されるものなのかは知りませんが、関心はもっぱらFlightGearではどのように記述したら積荷の重量変化を機体に反映させることができるのかに。
 何せ、外部燃料タンクやYasimの場合は、Weight and Fuel Settingsを用いてまがりなりにも再現することができたものの、JSBSimの場合には、どうすればよいものか、長いことさっぱりわからずにいたからです。

 F-100Dの機体ファイルを調べて、我が愛機Ju EF128でもこれが再現できるようにしたのが、下に掲載の画像です。

 核心は、<channel>タグを用い、<test>に記述された条件、すなわち爆弾などが切り離されたときに、当該pointmassの重量として<output>で0を出力することにあるようです。

 なお、F-100Dには、もう一つの特徴として、複数の武装をスイッチで選択し、同じ一つのボタンで切り離しを可能にするステーション・セレクト・パネルがあります。画像ではDavid Culp氏の作成したパネルを改造し、7つのトグルスイッチと、暗くても識別できるよう7つの対のランプを追加したパネルが、コクピット左に見えます。両端、すなわち両翼外側のミサイルが選択された状態になっています。今風にトグルスイッチ自体を光らせてもよいのでしょうが、レトロにしてみました。

 さて、同じJSBSimの機体であるPー51Dも、かなり武装の重量に気を配っているようなのですが、F-100Dとは正反対。Fー100Dでは、搭載される武器はあらかじめ決まっていて選べませんが、武器の使用によって生じる重量変化が機体に反映されるようになっているのに対し、Pー51Dでは、搭載する武器をセットの形である程度自由に選べることから、搭載する武器による重量の違いが機体に反映されるようになっています。ただし、武器の使用による重量変化は反映されていないようです。両方できてYasim並。

drop

↑上方2枚の画像:爆弾を投下すれば、投下された爆弾を吊り下げていた側の翼が浮き上がる。下方のコクピット画像:前面左側に見えるのがステーション・セレクト・パネル


 補足・修正。ミサイルの発射音を再現するsound.xmlもステーション名や装着位置にあったものに修正の必要をもらしていました。また、一部ファイル名に誤りがありました。なお、自身の覚書ですが、juef128ではじまるxmlファイル名は実際にはすべてjuef128の後に_customがつきます。たとえばsound.xmlファイルもAircraft/juef128/Sounds/juef128_custom-sound.xmlです。加えてsubmodel.xmlも実際にはAircraft/juef128/Models/submodels2.xmlです。
 あわせて、この機会にせめてarmament.xmlの一部でも紹介することにしたいと思います。

【実際】

〔武器の装着〕

◯機体の3Dモデル.xmlで武器の装着位置を指定

 元 Aircraft/F-100D/Models/F-100D.xml
 移植 Aircraft/juef128/Models/juef128.xmlを上記を参考に修正

◯装着位置指定に使われる武器の3Dモデルと.xmlファイルの例

 元 Aircraft/DavePack/Stores/mk82/mk82.ac
Aircraft/DavePack/Stores/mk82/mk82-sta3.xml
 移植 Aircraft/juef128/Models/Stores/Bombs/bomb.ac
Aircraft/juef128/Models/Stores/Bombs/bomb-sta3.xml


〔発射・投下後の武器の挙動〕

◯submodels.xmlに発射/投下後の各武器の初期位置など挙動に関するパラメーターを設定(実際には具体的な弾道などのパラメーターは、各武器毎のsubmodels.xmlをもうけてpath指定)

 元 Aircraft/F-100D/submodels.xml
 移植 Aircraft/juef128/Models/submodels.xmlを上記を参考に修正

◯なお、このsubmodels.xml中のにpsth指定される武器の3Dモデルと.xmlファイルの例は以下のとおり(1)に記した.xmlファイルとは異なることに注意)

 元 Aircraft/DavePack/Stores/mk82/mk82.ac
Aircraft/DavePack/Stores/mk82/mk82.xml
 移植 Aircraft/juef128/Models/Stores/Bombs/bomb.ac
Aircraft/juef128/Models/Stores/Bombs/bomb.xml

◯各武器毎のsubuModels.xmlの例

 元 Aircraft/DavePack/Stores/mk82/mk82-submodels.xml
 移植 Aircraft/juef128/Models/Stores/Bombs/bomb-submodels.xml

◯武器毎のsubmodels.xmlに記述された、武器のヒット時の爆発の再現にかかわるファイル(略)

◯ミサイルの発射音の再現にかかわるファイル(略)

〔ステーション選択スイッチ〕

◯station-select.xmlに武器を装着したステーションを選択するスイッチのON/OFFの挙動を記述

 元 Aircraft/DavePack/Instruments/Armament/station-select.xml
 移植 Aircraft/juef128/Models/Interior/Panel/Instruments/Armament/weapons-panel.acは上記を元に改造したもので、自機フォルダ内に配置

◯ステーション選択スイッチの位置を指定

 元 Aircraft/F-100D/Models/F-100D.xml
 移植 Aircraft/juef128/Models/juef128.xmlを修正


〔ホットスポット〕

◯ステーション選択スイッチをクリックして動かすためのpanel-hotspots.xml

 元 Aircraft/F-100D/Models/panel-hotspots.xml
 移植 Aircraft/juef128/Models/Interior/Panel/panel-hotspots.xmlを上記を参考に修正

◯ホットパネルの位置を指定

 元 Aircraft/F-100D/Models/F-100D.xml
 移植 Aircraft/juef128/Models/Interior/Panel/panel.xmlを修正


〔キーに関する設定、他〕

◯発射・投下キーの指定とヘルプの記述、submodelsやmaster-armの有効・無効、よくわかりませんが<trigger>のためと思われる<fdm>タグ内<armament>へのステーションの記述

 元 Aircraft/F-100D/Models/F-100D.xml
 移植 Aircraft/juef128/Models/Interior/Panel/panel.xmlを修正


〔当該武器の使用とそれに伴う重量変化の反映〕

◯<channel>で各ステーションの選択状況や発射・投下キーの状態を判別し、該当すれば当該武器の使用をアウトプット、また武器の使用の際には当該武器の重量を減少としてアウトプットしているとみられます(注2参照)

 元 Aircraft/F-100D/Systems/armament.xml
 移植 Aircraft/juef128/Systems/armament.xmlを上記を参考に新設(下記は一部抜粋)

 <?xml version="1.0"?>

 <system name="armament">

  <property value="0">systems/armament/release

  <channel name="Armament">

    <!-- release station 1 -->
    <switch name="systems/armament/station1/released">
      <default value="systems/armament/station1/released"/>
      <test logic="AND" value="1">
        systems/armament/release == 1
        systems/armament/station1/selected == 1
      </test>
      <output>systems/armament/station1/released
    </switch>

      (略)

 </channel>

 <channel name="Weight-loss">

    <!-- station 1 -->
    <switch name="systems/armament/station1/weight-control">
      <default value="inertia/pointmass-weight-lbs[0]"/>
      <test value="0">
        systems/armament/station1/released == 1
      </test>
      <output>inertia/pointmass-weight-lbs[0]
    </switch>

      (略)

 </channel>

 </system>



〔フライトモデル〕

◯機体のバランスにかかわるpointmassに各ステーションの武器の重量と重心を指定(注3参照)

 元 Aircraft/F-100D/F-100D-jsbsim.xml
 移植 Aircraft/juef128/juef128.xmlに書き加えと修正

注2)本来フライトモデルに記述されるべき内容をarmament.xmlとして外部ファイルに書き出したのであろう、フライトモデルを記述したF-100D-jsbsim.xmlには<system file=”armament”/>の1行が見られます。juef128.xmlにもこの1行が書き加えられている必要があります。

注3)ドロップタンクについては武器と多少扱いの違うところがあるためくわしくふれませんが、<contents>での重量の指定は0に。

[2012/07/22][2012/10/07補足][2012/12/01更新][2018/04/23刷新]

「仮想飛行」(virtual flight) by virt_fly