Problem Sikorsky R4 in C++ (port from Lua)

You must be using the wrong engine specification table. You need to use the one for the reciprocating engine (the one in my code). If you tried guessing numbers and putting them into the gas turbine table that's the only way this would happen.
I have translated your Lua tables into C++ structures, like this:

C++:
struct EngineSpec{
            
            double r = 5.20; //compression ratio

            int rc = 1; //cut-off ratio, 1 for Otto cycle, >1 for Diesel

            double displacement = 6.82e-3; //engine displacement in cubic meters

            int max_rpm = 2050; //maximum engine speed (rpm)

            int min_rpm = 750; //idle engine speed (rpm)

            int n_stroke = 4; //2 if 2-stroke, 4 if 4-stroke

            double HV = 43e6; //lower heating value of fuel (J/kg) (~43 MJ/kg for typical liquid fuels)

            double AF = 14.6;  //air fuel ratio (mass air/mass fuel) (stochiometric ~14.6:1 for typical liquid fuels)

        };
        EngineSpec engine_spec;

        struct GasTurbine_EngineSpec{

            int rp = 20; //pressure ratio

            int max_rpm = 3600; //maximum engine speed (rpm)

            int min_rpm = 1000; //idle engine speed (rpm)

            double max_air_flow = 69.4; //mass air flow at max rpm

            double HV = 43e6; //lower heating value of fuel (J/kg) (~43 MJ/kg for typical liquid fuels)

            double AF = 77.1; //air fuel ratio (mass air/mass fuel) (stochiometric ~14.6:1 for typical liquid fuels)
        };
        GasTurbine_EngineSpec gas_turbine_engine_spec;
 
I have translated your Lua tables into C++ structures, like this:

C++:
struct EngineSpec{
         
            double r = 5.20; //compression ratio

            int rc = 1; //cut-off ratio, 1 for Otto cycle, >1 for Diesel

            double displacement = 6.82e-3; //engine displacement in cubic meters

            int max_rpm = 2050; //maximum engine speed (rpm)

            int min_rpm = 750; //idle engine speed (rpm)

            int n_stroke = 4; //2 if 2-stroke, 4 if 4-stroke

            double HV = 43e6; //lower heating value of fuel (J/kg) (~43 MJ/kg for typical liquid fuels)

            double AF = 14.6;  //air fuel ratio (mass air/mass fuel) (stochiometric ~14.6:1 for typical liquid fuels)

        };
        EngineSpec engine_spec;

        struct GasTurbine_EngineSpec{

            int rp = 20; //pressure ratio

            int max_rpm = 3600; //maximum engine speed (rpm)

            int min_rpm = 1000; //idle engine speed (rpm)

            double max_air_flow = 69.4; //mass air flow at max rpm

            double HV = 43e6; //lower heating value of fuel (J/kg) (~43 MJ/kg for typical liquid fuels)

            double AF = 77.1; //air fuel ratio (mass air/mass fuel) (stochiometric ~14.6:1 for typical liquid fuels)
        };
        GasTurbine_EngineSpec gas_turbine_engine_spec;
The engine models for power don't even use the same parameters. If you give them the spec of the other engine type it will fail.

Comment out the gas turbine spec and the brayton cycle model code and use only the reciprocating engine model. You may be calculating power for both engine types simultaneously.
 
The engine models for power don't even use the same parameters. If you give them the spec of the other engine type it will fail.

Comment out the gas turbine spec and the brayton cycle model code and use only the reciprocating engine model. You may be calculating power for both engine types simultaneously.
I've commented out the functions, now I'll try compiling. What strikes me is that I wasn't calling those functions anywhere, despite them being written in the source code. I don't think they were even being executed.
 
If you want to use the gas turbine engine I had to make some minor changes, might be easier to port those (in att. on other thread).
 
The engine models for power don't even use the same parameters. If you give them the spec of the other engine type it will fail.

Comment out the gas turbine spec and the brayton cycle model code and use only the reciprocating engine model. You may be calculating power for both engine types simultaneously.
Oh my God!
Blessed are subtle errors in C++!

In this little line I was dividing two integers with the intention of getting a double.
C++:
double mass_flow_air = air_density * displacement * (2.0 / n_stroke) * rpm * (1.0 / 60); //air flow in kg/s

And remember kids:
"C makes it easy to shoot yourself in the foot; C++ makes it harder, but when you do it blows your whole leg off"
-
Bjarne Stroustrup
 
Oh my God!
Blessed are subtle errors in C++!

In this little line I was dividing two integers with the intention of getting a double.
C++:
double mass_flow_air = air_density * displacement * (2.0 / n_stroke) * rpm * (1.0 / 60); //air flow in kg/s

And remember kids:
"C makes it easy to shoot yourself in the foot; C++ makes it harder, but when you do it blows your whole leg off"
-
Bjarne Stroustrup
But that is for the gas turbine engine. Use only the reciprocating engine!
 

The most latest C++ language standard that Visual C++ supports. The latest C++ standard is c++23, which is currently not supported by many compilers.
Not sure how to tell which version I am using?

R4.CPP
1>The contents of <format> are available only with C++20 or later.
 
When I compilied I get these errors:

R4.CPP
1>The contents of <format> are available only with C++20 or later.
1>D:\orbiter2024\Orbitersdk\samples\BLACKHAWKuh60new\R4.CPP(345,13): warning C4996: 'sscanf':

errors
Severity Code Description Project File Line Suppression State Details
Error C2039 'format': is not a member of 'std' R4 D:\orbiter2024\Orbitersdk\samples\BLACKHAWKuh60new\R4.CPP 787
Error C3861 'format': identifier not found R4 D:\orbiter2024\Orbitersdk\samples\BLACKHAWKuh60new\R4.CPP 787

if (color.a != 0) {

std::string col_fmt = std::format("r=%lf, g=%lf, b=%lf, a=%lf", color.r, color.g, color.b, color.a);
char* ch_col = new char[col_fmt.length() + 1];
std::strcpy(ch_col, col_fmt.c_str());

oapiWriteScenario_string(scn, ch_color, ch_col);
}
if I comment it out it loads but no meshes are found.
000010.236: ============================ ERROR: ===========================
000010.236: Mesh not found: .\Meshes\R-4/Fuselage.msh
000010.237: [MeshManager::LoadMesh | D:\a\orbiter\orbiter\Src\Orbiter\Mesh.cpp | 1307]
000010.237: ===============================================================
000010.238: Module R4.dll ................ [Build 250318, API 241231]
000010.238: ============================ ERROR: ===========================
000010.238: Mesh not found: .\Meshes\R-4/Fuselage.msh
000010.238: [MeshManager::LoadMesh | D:\a\orbiter\orbiter\Src\Orbiter\Mesh.cpp | 1307]
000010.238: ===============================================================
000010.239: Finished initialising status
000010.240: Finished initialising camera
000010.240: Finished setting up render state
000010.243: ============================ ERROR: ===========================
000010.243: Mesh not found: .\Meshes\R-4/Gears.msh
000010.243: [MeshManager::LoadMesh | D:\a\orbiter\orbiter\Src\Orbiter\Mesh.cpp | 1307]
000010.243: ===============================================================
000010.243: ============================ ERROR: ===========================
000010.243: Mesh not found: .\Meshes\R-4/Gears.msh
000010.244: [MeshManager::LoadMesh | D:\a\orbiter\orbiter\Src\Orbiter\Mesh.cpp | 1307]
000010.244: ===============================================================
000010.260: Finished initialising panels
000010.271: ============================ ERROR: ===========================
000010.271: Mesh not found: .\Meshes\.msh
000010.271: [LoadMesh | D:\a\orbiter\orbiter\Src\Orbiter\Mesh.cpp | 1341]
000010.271: ===============================================================
000010.271: ============================ ERROR: ===========================
000010.271: Mesh not found: .\Meshes\.msh
000010.271: [LoadMesh | D:\a\orbiter\orbiter\Src\Orbiter\Mesh.cpp | 1341]
000010.272: ===============================================================
000027.087: Unloading module VenusAtm2006
000027.088: Unloading module EarthAtmJ71G
000027.088: Unloading module MarsAtm2006
000027.113: **** Closing simulation session
 
Yes, you need to compile the project with the settings for C++20.


 
Thanks. I changed it to this
That fixed the format issue.
I only get this:
1>D:\orbiter2024\Orbitersdk\samples\BLACKHAWKuh60new\R4.CPP(345,13): warning C4996: 'sscanf': This function or variable may be unsafe. Consider using sscanf_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.

My mistake. I loaded the 2.0 version and now the meshes are seen.

No help screen though?
 

Attachments

  • c++.jpg
    c++.jpg
    63.1 KB · Views: 2
Last edited:
Thats a warning, so you can ignore it or simply switch to the more safe version, that is suggested there. The alternatives are C standard (not the same as C++!) since C11.


You can happily mix C and C++ code, but the library versions may be a bit surprising then, since a compiler must only support C++23 with C17, but must not already include C23, the current C language standard. So, if one compiler already has C23 features implemented (or removed obsolete, like ye olde K&R function declarations) and does not warn you about using them, its still better to stick to anything really in C17, so your code can be compiled by other compilers or older compiler versions.
 
I got the animation of the gauges to work. But on mine. I do not get lights, help screen, and I don't get pitch,.... control. I gat use thrust and collective but can't change the heading,.....
 
I got the animation of the gauges to work. But on mine. I do not get lights, help screen, and I don't get pitch,.... control. I gat use thrust and collective but can't change the heading,.....
Tomorrow I'll implement your animation fixes, and I'll provide you with a DLL so you can try.
 
So I am running the r4 scripted and a r4 dll version. For Me I get no pitch, roll, rudder action on the dll version. The scripted version it works. Not sure what is going on.

CreateControlSurface(AIRCTRL_ELEVATOR, 0, 0, _V(0, 0, 0), AIRCTRL_AXIS_AUTO, 1);

CreateControlSurface(AIRCTRL_AILERON, 0, 0, _V(0, 0, 0), AIRCTRL_AXIS_AUTO, 1);

CreateControlSurface(AIRCTRL_RUDDER, 0, 0, _V(0, 0, 0), AIRCTRL_AXIS_AUTO, 2);

//Get flight control inputs

pitch = GetControlSurfaceLevel(AIRCTRL_ELEVATOR);

roll = GetControlSurfaceLevel(AIRCTRL_AILERON);

tail_rotor_dir = -GetControlSurfaceLevel(AIRCTRL_RUDDER);

tail_rotor_spec.prop_eff = std::abs(tail_rotor_dir);

throttle_level = GetThrusterGroupLevel(THGROUP_MAIN);

main_rotor_spec.prop_eff = GetThrusterGroupLevel(THGROUP_HOVER);
 
So I am running the r4 scripted and a r4 dll version. For Me I get no pitch, roll, rudder action on the dll version. The scripted version it works. Not sure what is going on.

CreateControlSurface(AIRCTRL_ELEVATOR, 0, 0, _V(0, 0, 0), AIRCTRL_AXIS_AUTO, 1);

CreateControlSurface(AIRCTRL_AILERON, 0, 0, _V(0, 0, 0), AIRCTRL_AXIS_AUTO, 1);

CreateControlSurface(AIRCTRL_RUDDER, 0, 0, _V(0, 0, 0), AIRCTRL_AXIS_AUTO, 2);

//Get flight control inputs

pitch = GetControlSurfaceLevel(AIRCTRL_ELEVATOR);

roll = GetControlSurfaceLevel(AIRCTRL_AILERON);

tail_rotor_dir = -GetControlSurfaceLevel(AIRCTRL_RUDDER);

tail_rotor_spec.prop_eff = std::abs(tail_rotor_dir);

throttle_level = GetThrusterGroupLevel(THGROUP_MAIN);

main_rotor_spec.prop_eff = GetThrusterGroupLevel(THGROUP_HOVER);
The flight control definitions only take the user control input. All of the coefficients and areas are set to 0 so they don't apply force. This is by design. Pitch and roll are controlled by tilting the rotor thrust vector in clbk_prestep.
 
Thanks.

But for me On the ground the rudder control should steer the copter, right? In flight I get no control

//Get flight control inputs

pitch = GetControlSurfaceLevel(AIRCTRL_ELEVATOR);

roll = GetControlSurfaceLevel(AIRCTRL_AILERON);

tail_rotor_dir = -GetControlSurfaceLevel(AIRCTRL_RUDDER);

GetEngine_ReciprocatingPower(efficiency, engine_spec, main_fuel_tank, throttle_level);

double main_rotor_thrust = GetPropeller_Thrust(main_rotor_spec, 0.9 * power, airspd.y);

main_rotor_thrust_vec = GetHelp_Rotate(_V(0, main_rotor_thrust, 0), pitch * 6 * RAD, 0, roll * 6 * RAD);

AddForce(main_rotor_thrust_vec, operator-(main_rotor_axis, cg));
 
Thanks.

But for me On the ground the rudder control should steer the copter, right? In flight I get no control

//Get flight control inputs

pitch = GetControlSurfaceLevel(AIRCTRL_ELEVATOR);

roll = GetControlSurfaceLevel(AIRCTRL_AILERON);

tail_rotor_dir = -GetControlSurfaceLevel(AIRCTRL_RUDDER);

GetEngine_ReciprocatingPower(efficiency, engine_spec, main_fuel_tank, throttle_level);

double main_rotor_thrust = GetPropeller_Thrust(main_rotor_spec, 0.9 * power, airspd.y);

main_rotor_thrust_vec = GetHelp_Rotate(_V(0, main_rotor_thrust, 0), pitch * 6 * RAD, 0, roll * 6 * RAD);

AddForce(main_rotor_thrust_vec, operator-(main_rotor_axis, cg));
You're missing the add_force for the tail rotor.
 
Ok I have this. pitch and roll are always 0 no matter if collective is increased?

void R4::clbkPreStep(double simt, double simdt, double mjd) {

VECTOR3 airspd = _V(0, 0, 0);
GetAirspeedVector(FRAME_LOCAL, airspd);

//Get flight control inputs

pitch = GetControlSurfaceLevel(AIRCTRL_ELEVATOR);

roll = GetControlSurfaceLevel(AIRCTRL_AILERON);

tail_rotor_dir = -GetControlSurfaceLevel(AIRCTRL_RUDDER);

tail_rotor_spec.prop_eff = std::abs(tail_rotor_dir);

throttle_level = GetThrusterGroupLevel(THGROUP_MAIN);

main_rotor_spec.prop_eff = GetThrusterGroupLevel(THGROUP_HOVER);

//Set engine power and rotor thrust

GetEngine_ReciprocatingPower(efficiency, main_fuel_tank, throttle_level);

double main_rotor_thrust = GetPropeller_Thrust_MainRotor(0.9 * power, airspd.y);

main_rotor_thrust_vec = GetHelp_Rotate(_V(0, main_rotor_thrust, 0), pitch * 6 * RAD, 0, roll * 6 * RAD);

AddForce(main_rotor_thrust_vec, operator-(main_rotor_axis, cg));

//Main rotor axial torque (realistic, but hard to fly with keyboard)

//main_rotor_torque = main_engine_torque/main_rotor_ratio

//vi:add_force({x=0, y=0, z=-main_rotor_torque}, {x= 0.5, y=main_rotor_axis.y, z=main_rotor_axis.z})
//vi:add_force({x=0, y=0, z= main_rotor_torque}, {x=-0.5, y=main_rotor_axis.y, z=main_rotor_axis.z})

//Get tail rotor thrust and net torque
double tail_rotor_thrust = GetPropeller_Thrust_TailRotor(0.1 * power, airspd.x);

VECTOR3 tail_rotor_thrust_vec = _V(tail_rotor_dir * tail_rotor_thrust, 0, 0);

VECTOR3 tail_rotor_torque = crossp(tail_rotor_thrust_vec, tail_rotor_axis);


AddForce(_V(0, 0, tail_rotor_torque.y), _V(0.5, main_rotor_axis.y, main_rotor_axis.z));

AddForce(_V(0, 0, -tail_rotor_torque.y), _V(-0.5, main_rotor_axis.y, main_rotor_axis.z));

sprintf(oapiDebugString(), "pitch %2.2f rudder %2.2f", pitch, tail_rotor_dir);
 

Attachments

  • r4nopitch.jpg
    r4nopitch.jpg
    40.8 KB · Views: 4
Ok I have this. pitch and roll are always 0 no matter if collective is increased?

void R4::clbkPreStep(double simt, double simdt, double mjd) {

VECTOR3 airspd = _V(0, 0, 0);
GetAirspeedVector(FRAME_LOCAL, airspd);

//Get flight control inputs

pitch = GetControlSurfaceLevel(AIRCTRL_ELEVATOR);

roll = GetControlSurfaceLevel(AIRCTRL_AILERON);

tail_rotor_dir = -GetControlSurfaceLevel(AIRCTRL_RUDDER);

tail_rotor_spec.prop_eff = std::abs(tail_rotor_dir);

throttle_level = GetThrusterGroupLevel(THGROUP_MAIN);

main_rotor_spec.prop_eff = GetThrusterGroupLevel(THGROUP_HOVER);

//Set engine power and rotor thrust

GetEngine_ReciprocatingPower(efficiency, main_fuel_tank, throttle_level);

double main_rotor_thrust = GetPropeller_Thrust_MainRotor(0.9 * power, airspd.y);

main_rotor_thrust_vec = GetHelp_Rotate(_V(0, main_rotor_thrust, 0), pitch * 6 * RAD, 0, roll * 6 * RAD);

AddForce(main_rotor_thrust_vec, operator-(main_rotor_axis, cg));

//Main rotor axial torque (realistic, but hard to fly with keyboard)

//main_rotor_torque = main_engine_torque/main_rotor_ratio

//vi:add_force({x=0, y=0, z=-main_rotor_torque}, {x= 0.5, y=main_rotor_axis.y, z=main_rotor_axis.z})
//vi:add_force({x=0, y=0, z= main_rotor_torque}, {x=-0.5, y=main_rotor_axis.y, z=main_rotor_axis.z})

//Get tail rotor thrust and net torque
double tail_rotor_thrust = GetPropeller_Thrust_TailRotor(0.1 * power, airspd.x);

VECTOR3 tail_rotor_thrust_vec = _V(tail_rotor_dir * tail_rotor_thrust, 0, 0);

VECTOR3 tail_rotor_torque = crossp(tail_rotor_thrust_vec, tail_rotor_axis);


AddForce(_V(0, 0, tail_rotor_torque.y), _V(0.5, main_rotor_axis.y, main_rotor_axis.z));

AddForce(_V(0, 0, -tail_rotor_torque.y), _V(-0.5, main_rotor_axis.y, main_rotor_axis.z));

sprintf(oapiDebugString(), "pitch %2.2f rudder %2.2f", pitch, tail_rotor_dir);
Pitch and roll are not affected by the collective. They simply are used to tilt the main rotor thrust vector (the magnitude of which is controlled by the collective) so you can pitch and roll the vessel.

You need to be clear on whether pitch and roll are 0 and not changing, or is the main thrust 0. You need both working to get thrust and directional control.

Do the following:
  • Set text output to indicate the values of pitch and roll. Pitching up and down with normal Orbiter elevator controls and rolling with aileron controls should cause these values to change. When you release the controls both of these will go to 0.
  • Set text output to report power. When the Orbiter throttle is increased that should increase. Report what you get at full throttle.
  • Set text output to report main_rotor_thrust. At full engine power, increase the collective. This number should increase with the collective.
This should tell us where the problems are.
 
Last edited:
Back
Top