JSBSim-Team / jsbsim

An open source flight dynamics & control software library
GNU Lesser General Public License v2.1
1.27k stars 435 forks source link

[Documentation] Looking for guidance on inputs #396

Open chunky opened 3 years ago

chunky commented 3 years ago

I'm looking at using JSBSim as the FDM in a tool [in C++] that a human will interact directly with. I'm really lost as to the right way to go about doing this.

I don't imagine hinging on another app across a socket is the best way to do a first-class integration with a C++ program, but is it even the right approach to focus on setting values in trees?

Really I desperately want a 50-ish[?] line C++ example that loads an aircraft then runs a loop where some small number of inputs are collected from a user, they affect change in the model, then outputs, say, simple lat/lon/alt coordinates.

seanmcleod commented 3 years ago

I've implemented a semi-custom flight simulator using JSBSim. I went with the basic copy_to_JSBSim() and copy_from_JSBSim() approach.

So in the main loop in JSBSim.cpp I added calls to copy_to_JSBSim() and copy_from_JSBSim().

        // Copy control inputs in
        copy_to_JSBSim();

        for (int i=0; i<(int)(sim_lag_time/frame_duration); i++) {  // catch up sim time to actual elapsed time.
          result = FDMExec->Run();
          cycle_duration = getcurrentseconds() - current_seconds;   // Calculate cycle duration
          current_seconds = getcurrentseconds();                    // Get new current_seconds
          if (FDMExec->Holding()) break;
        }

        // Copy JSBSim model data out
        copy_from_JSBSim();

Then in my implementation I grab the joystick inputs and feed them into JSBSim. In my case there is a lot of code within my joystick class since I'm dealing with programmable force feedback inceptors via an ethernet to CAN interface etc. I've removed a lot of code to do with trimming etc. so the function basically boils down to this.

void copy_to_JSBSim()
{
    std::shared_ptr<JSBSim::FGFCS> FCS = FDMExec->GetFCS();

    g_Joystick.Poll();
    FCS->SetDeCmd(g_Joystick.Pitch);
    FCS->SetDaCmd(g_Joystick.Roll);
    FCS->SetDrCmd(g_Joystick.Rudder);
    FCS->SetThrottleCmd(-1, g_Joystick.Throttle);
    FCS->SetDsbCmd(g_Joystick.SpeedBrake);
}

In my setup I need to get data like the aircraft's attitude etc. in order to render a custom PFD (Primary Flight Display) running on a separate PC on the network, so my copy_from_JSBSim() grabs the relevant state and sends it over the network in my own custom network packet.

Depending on what you need to do with the state outputs you could have an empty copy_from_JSBSim() function if you're happy to say just have the state logged to a CSV, or make use of one of the other existing <Output> options, for example see the following comment regarding the socket options for <Output> - https://github.com/JSBSim-Team/jsbsim/discussions/393#discussioncomment-356175

void copy_from_JSBSim()
{
    // Collect JSBSim data
    std::shared_ptr<JSBSim::FGPropagate> Propagate = FDMExec->GetPropagate();
    std::shared_ptr<JSBSim::FGAuxiliary> Auxiliary = FDMExec->GetAuxiliary();
    std::shared_ptr<JSBSim::FGAtmosphere> Atmosphere = FDMExec->GetAtmosphere();
    std::shared_ptr<JSBSim::FGAccelerations> Accelerations = FDMExec->GetAccelerations();

    AircraftData.Altitude = Propagate->GetAltitudeASL();
    AircraftData.Latitude = Propagate->GetLocation().GetLatitudeDeg();
    AircraftData.Longitude = Propagate->GetLocation().GetLongitudeDeg();

    AircraftData.Bank = radtodeg * Propagate->GetEuler(ePhi) * -1.0;
    AircraftData.Pitch = radtodeg * Propagate->GetEuler(eTht) * -1.0;
    AircraftData.Heading = radtodeg * Propagate->GetEuler(ePsi);

..........
}

In my case I have an instructor station application running on a laptop on the network which allows the user to select a particular lesson to launch.

So the instructor station application generates an initialization (starting pose, speed etc.) file for the specific aircraft model selected and generates a simple script file and then launches JSBSim.exe remotely passing the name of the script file.

The existing code in JSBSim will then load the relevant aircraft model specified in the script file and initialize it using the initialization file specified in the script file.

So in my case I don't have specific C++ code to load a particular model, initialize it etc. But if you do want to you'll find the relevant code in JSBSim.cpp.

But the above should serve as a 50-ish line example 😉

chunky commented 3 years ago

This is wonderful, it's exactly what I needed.

I would recommend finding a place to add this to the broader documentation - I suspect my question is not uncommon. I'll leave this bug open in case you want to use it to track such a documentation change, but from my perspective your reply fully resolves my immediate issue.

Thank-you!

seanmcleod commented 3 years ago

Yep about 3 years ago @agodemar started a repo to redo/update the original PDF documentation as a set of Github pages, so we need to continue the process 😉

https://jsbsim-team.github.io/jsbsim-reference-manual/

https://github.com/JSBSim-Team/jsbsim-reference-manual

agodemar commented 3 years ago

Yeah, the part named "Programmer manual" is very much needed

beantowel commented 3 years ago

I've implemented a semi-custom flight simulator using JSBSim. I went with the basic copy_to_JSBSim() and copy_from_JSBSim() approach.

So in the main loop in JSBSim.cpp I added calls to copy_to_JSBSim() and copy_from_JSBSim().

        // Copy control inputs in
        copy_to_JSBSim();

        for (int i=0; i<(int)(sim_lag_time/frame_duration); i++) {  // catch up sim time to actual elapsed time.
          result = FDMExec->Run();
          cycle_duration = getcurrentseconds() - current_seconds;   // Calculate cycle duration
          current_seconds = getcurrentseconds();                    // Get new current_seconds
          if (FDMExec->Holding()) break;
        }

        // Copy JSBSim model data out
        copy_from_JSBSim();

Then in my implementation I grab the joystick inputs and feed them into JSBSim. In my case there is a lot of code within my joystick class since I'm dealing with programmable force feedback inceptors via an ethernet to CAN interface etc. I've removed a lot of code to do with trimming etc. so the function basically boils down to this.

void copy_to_JSBSim()
{
    std::shared_ptr<JSBSim::FGFCS> FCS = FDMExec->GetFCS();

    g_Joystick.Poll();
    FCS->SetDeCmd(g_Joystick.Pitch);
    FCS->SetDaCmd(g_Joystick.Roll);
    FCS->SetDrCmd(g_Joystick.Rudder);
    FCS->SetThrottleCmd(-1, g_Joystick.Throttle);
    FCS->SetDsbCmd(g_Joystick.SpeedBrake);
}

In my setup I need to get data like the aircraft's attitude etc. in order to render a custom PFD (Primary Flight Display) running on a separate PC on the network, so my copy_from_JSBSim() grabs the relevant state and sends it over the network in my own custom network packet.

Depending on what you need to do with the state outputs you could have an empty copy_from_JSBSim() function if you're happy to say just have the state logged to a CSV, or make use of one of the other existing <Output> options, for example see the following comment regarding the socket options for <Output> - #393 (comment)

void copy_from_JSBSim()
{
    // Collect JSBSim data
    std::shared_ptr<JSBSim::FGPropagate> Propagate = FDMExec->GetPropagate();
    std::shared_ptr<JSBSim::FGAuxiliary> Auxiliary = FDMExec->GetAuxiliary();
    std::shared_ptr<JSBSim::FGAtmosphere> Atmosphere = FDMExec->GetAtmosphere();
    std::shared_ptr<JSBSim::FGAccelerations> Accelerations = FDMExec->GetAccelerations();

    AircraftData.Altitude = Propagate->GetAltitudeASL();
    AircraftData.Latitude = Propagate->GetLocation().GetLatitudeDeg();
    AircraftData.Longitude = Propagate->GetLocation().GetLongitudeDeg();

    AircraftData.Bank = radtodeg * Propagate->GetEuler(ePhi) * -1.0;
    AircraftData.Pitch = radtodeg * Propagate->GetEuler(eTht) * -1.0;
    AircraftData.Heading = radtodeg * Propagate->GetEuler(ePsi);

..........
}

In my case I have an instructor station application running on a laptop on the network which allows the user to select a particular lesson to launch.

So the instructor station application generates an initialization (starting pose, speed etc.) file for the specific aircraft model selected and generates a simple script file and then launches JSBSim.exe remotely passing the name of the script file.

The existing code in JSBSim will then load the relevant aircraft model specified in the script file and initialize it using the initialization file specified in the script file.

So in my case I don't have specific C++ code to load a particular model, initialize it etc. But if you do want to you'll find the relevant code in JSBSim.cpp.

But the above should serve as a 50-ish line example 😉

This is more like importing our codes into JSBSim than using JSBSim as a library in our codes. In the second scenario, the "main" loop was supposed to be defined in our application.

chunky commented 3 years ago

@beantowel the code at the top, there ["copy inputs to jsb; {simulate steps}; copy jsb to outputs"] is a function that you call from your program, with an appropriate deltaT parameter.

The loop in that code isn't the main loop, it's just there to deal with the possibility that your program's deltaT doesn't necessarily align with JSBSim's configured deltaT [or can be called with differing timesteps, wheras JSB needs a fixed timestep]