Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Documentation] Looking for guidance on inputs #396

Open
chunky opened this issue Feb 17, 2021 · 6 comments
Open

[Documentation] Looking for guidance on inputs #396

chunky opened this issue Feb 17, 2021 · 6 comments

Comments

@chunky
Copy link

chunky commented Feb 17, 2021

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
Copy link
Member

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 😉

@chunky
Copy link
Author

chunky commented Feb 17, 2021

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
Copy link
Member

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
Copy link
Contributor

agodemar commented Feb 17, 2021

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

@beantowel
Copy link

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
Copy link
Author

chunky commented Mar 16, 2021

@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]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants