Professional Documents
Culture Documents
SimConnect Tutorial 0-800
SimConnect Tutorial 0-800
SimConnect Tutorial 0-800
--------------Follow
How to fly
This Plane
AI Aircrafts
SimConnect
Using
Tutorial
Waypoints
For Flight
in FSX SDK
Simulator X ---------------
N0800 Tutorial
program. The console output of the program shows what we are doing.
What is this tutorial for? The tutorial is to understand how we send nearby waypoints What do you need to complete this tutorial? MS FSX SDK, which is on the CD
to a second aircraft, to fly it smoothly towards the waypoints. Well also drop a parachute of MS FSX Professional. A compiler, which can be MS Visual Studio C++, C# or VB,
at the location of the waypoints, to visualize where is the second aircraft flying to. In the
which is available for free at Microsoft.
picture below, our aircraft is on top and the second aircraft is controlled by our SimConnect
SimConnect Overview
SimConnect is a gateway that allows external programs to interact with FSX.
SimConnect is part of FSX SDK. So you need to install the SDK if you want to use
SimConnect. It displays itself as a server and the external programs are clients.
SimConnect allows the client to receive information about FSX simulation. The
client may ask details of the current state of the simulation, for instance which objects,
like aircrafts, are currently simulated; or what are the parameters, like the altitude, of
these objects.
An asynchronous request / reply mechanism is used to obtain data. The client
usually sends a request to SimConnect, SimConnect replies to this request later. The client
hasnt to wait for the reply, it can perform other tasks in the meantime. When SimConnect
is ready to reply, it sends an event message to the client, using a messaging system.
The client needs to receive all event messages, and dispatch them according
to their nature. To do that it monitors continuously for new SimConnect messages thru
an endless loop. When a message is available, the loop transfers the control to a callback
routine designed to handle these messages in a specific way.
The callback procedure is the core of the client design. It identifies the type of
message received, and execute appropriate calls so that the application is aware of replies
to previous requests. Basically a SimConnect client application is a program that sends
requests to SimConnect and manage replies in an asynchronous way in the callback
procedure.
The client may also subscribe for events notification. It informs SimConnect
of its interest in being informed about changes, e.g. the simulation being paused, or
an object being removed from the simulation. When such event occurs, SimConnect
just sends an event message to be received and processed by the client in the callback
procedure.
SimConnect allows the client to change the status of the simulation. The client
can move objects, create new ones, control cameras, modify FSX menus, display dialog
boxes, etc. To do that, the client sends data, aimed to some object in the simulation.
SimConnect receives these data asynchronously too. It processes them when possible, and
according to some priority set by the client.
SimConnect manages multiple clients. Clients may reside on the same computer or
void C A LL B A C K
P ro g ra m S ta rt
M yD isp atch P ro c
(S IM C O N N E C T_ R E C V * pD ata)
C ontinue = true
s w itc h(pD ata->dw ID )
S leep()
C ontinue = = true
c a se S IM C O N N EC T_R E C V_ ID _ E XC E P T IO N :
bre a k;
c a se S IM C O N N EC T_R E C V_ ID _ E V EN T :
bre a k;
c a se S IM C O N N EC T_R E C V_ ID _ E V EN T _O BJ E C T _ AD D R EM O V E :
bre a k;
c a se S IM C O N N EC T_R E C V_ ID _ S IM O BJ EC T_ D AT A :
bre a k;
c a se
P ro g ra m E n d
R o u tin e E n d
not. The connection between the server and the clients relies on IP streams and pipes,
but these details are mostly transparent to the clients programmers. The SimConnect
API manages the communication when data transfers are needed. Clients may ask other
clients to be notified of what they send to SimConnect. The FSX simulation engine by itself
is also a SimConnect client. All messages inbound and outbound are processed by the
server based on priorities that may be assigned by clients. The final order for processing
messages of identical priority is managed by SimConnect.
SimConnect Documentation
The main documentation for SimConnect use is the Help file included in FSX SDK.
Unfortunately this documentation is disappointing, it is not at all aimed to start
programming SimConnect applications, it is just the raw documentation of the
SimConnect API, in alphabetical order of the functions. In addition, it is definetely C++
oriented, and leaves the C# or VB programmer with additional difficulties related to calling
the C API from a non C application. Additional mechanisms are needed to check / convert
/ validate data types being sent to SimConnect by the client (MS calls that marshalling).
Lets see with this C portion of code:
struct Struct1
{
char* title;
double latitude;
double longitude;
double altitude;
};
In the SDK documentation youll find a small set of examples in C# and VB. However all
the API is described using C approach. So be prepared to face additional challenges if you
have or want to use compilers producing managed code for the .NET framework.
When it comes to searching the internet for answers not found in the SDK documentation,
youll also feel that there is a very limited number of urls reported by your prefered search
engine. Most of them point to MSDN. Many of those pages will discuss something not
working as it should, or will ask for explanations about how things should be done. You
may end thinking this is just emphasizing the fact that there are so many functions in
the API, and so little overview on how to use them. And no SimConnect Primer available
so far. All things considered, you got FSX SDK in FSX professional for a mere 10 bucks
compared to the standard version. Thats OK!
By the way, after MS realized Flight Simulator was a valuable platform for simulation
of any kind, and could be a gold mine in the serious companies sectors, they started
working on the generic simulation engine Enterprise Simulation Platform. ESP has now
several versions, but for the time being the documentation related to ESP can be used for
Of course, you need to install the FSX SDK if you didnt install it with FSX initially.
By creating a SimConnect.ini file in your My Documents\Flight Simulator X
Files folder you can also enable a debug window in FSX. This is briefly explained in the
help file. You can copy here the default ini file found in the SDK folder. No need to create
one from scratch.
simulation. Such events may need to be reported by the server to all or some of its clients
(5).
At this time, you now have the blue part of this diagram (1, 2 and 3):
If FSX is terminated by the user (or for some other reason), a message will be sent by the
server to the clients.
2
C++
S ource
3
In the end the client has to close the connection with the server to free any allocated
resources.
1
S im C o nnect
A P I H eader
On his side, the user (8) is controlling FSX. If a client wants to be informed when some
users event occurs, it may subscribe to it. Such events detected by the simulation engine
(9) will be transferred to the server and then to the client.
V isual
C++
We mentionned the server informing (calling back) the client of two events: the
connection process success and FSX being terminated. Technically this is a call of the
clients callback procedure, with a parameter being a pointer to a message structure. All
messages are declared this way: SIMCONNECT_RECV* pData.
C om pile T im e
R un T im e
F S X U ser
4
8
5
7
FSX
S im ulation
E ngine
S im C onnect
S erv er
S im C onnect
A pplication (C lient)
10
6
S im C o nnect
A P I Library
The SimConnect API header (3) contains all the declarations needed to work with the
API (6). After you compile your code and link it (4) with the API library, you can run the
executable client (5). This is how it will work:
(5) the client opens a connection with the server (7). When the connection request has
been processed successfully, the server post a message (10) to the client to inform it. The
client can now send requests and data to the server.
To answer such request the server (7) may need to talk with FSX (9).
Conversely FSX (9) will have to inform the server (7) about events occuring in the
SIMCONNECT_RECV definition:
struct SIMCONNECT_RECV
{
DWORD
dwSize;
DWORD
dwVersion;
DWORD
dwID;
};
// record size
// interface version
// see SIMCONNECT_RECV_ID
dwID is an integer identifying the kind of message being received. For a feedback after
a connection request it will be 2, for FSX quitting it will be 3. We wont manipulate these
values directly. Instead well use the enumerated values provided in SimConnect.h:
// Receive data types
enum SIMCONNECT_RECV_ID {
SIMCONNECT_RECV_ID_NULL,
SIMCONNECT_RECV_ID_EXCEPTION,
SIMCONNECT_RECV_ID_OPEN,
SIMCONNECT_RECV_ID_QUIT,
SIMCONNECT_RECV_ID_EVENT,
SIMCONNECT_RECV_ID_EVENT_OBJECT_ADDREMOVE,
SIMCONNECT_RECV_ID_EVENT_FILENAME,
SIMCONNECT_RECV_ID_EVENT_FRAME,
SIMCONNECT_RECV_ID_SIMOBJECT_DATA,
...
SIMCONNECT_RECV_ID_EVENT_RACE_LAP,
};
No field is added.
An answer to a request for the altitude of our aircraft will be more interesting.
So well move on and discuss our real example. Since every client must open a connection
to SimConnect, lets see how we do that and how we handle the reply from the server.
Message loop
Actually lets construct the first bricks of our masterpiece ;-) starting with the constructor
of our main class (FollowThisPlane). As you recall, we need to have an endless loop
that wait for incoming message posted by the server, and transfer them to a callback
procedure. Well execute this loop just after we have requested to open a connection with
the server:
HANDLE hSimConnect = NULL;
bool
keep_going = false;
// Constructor. Establish a connection with SimConnect
void FollowThisPlane()
{
// Connect to FSX
Connect();
if (hSimConnect != NULL)
{
// Connected. Loop until FSX exits
keep_going = true;
while(keep_going)
{
SimConnect_CallDispatch(hSimConnect,
MyDispatchProc, NULL);
Sleep(1);
}
// FSX exited. Close the connection with SimConnect
Disconnect();
}
else
{
// Not able to connect. Not retrying in this version
}
// (the program now quits)
}
The call Connect(); will initiate a connection, but the outcome of this request will
arrive asynchronously later. Well have a look at what we do within this call. When
Connect() returns, the variable hSimConnect will have been positionned by
SimConnect when requesting to open a connection. In case we were not able to talk
to SimConnect, this variable will remain NULL. In this case, our constructor will return
and the programm will quit. It can happen if FSX is not active when we run our little
SimConnect client.
}
else
{
In case we were able to post our request to open a connection, well set keep_going to
true and enter an infinite loop. In fact well need to have another part of the program
resetting this flag to false at some point, so that we can exit the loop and terminate the
program.
Until then, in this loop well call SimConnect_CallDispatch(hSimConnect,
MyDispatchProc, NULL) and then Sleep(1). SimConnect_CallDispatch
just looks for the next SimConnect message waiting in the connection queue associated
with hSimConnect and instructs SimConnect to call another portion of our code
(MyDispatchProc) to handle this message.
Bear in mind that this function excepted, a SimConnect client is completely asynchronous.
The client waits for a message to arrive, and at the same time performs other tasks, likely
in relation with the messages already received. This means the client must have different
threads working in parallel.The constructors thread is likely to be waiting most of the
time. SimConnect will call MyDispatchProc() in a new thread.
When SimConnect_CallDispatch() returns, we just Sleep() for 1 ms to ensure
other processes can run.
When the callback procedure is called by SimConnect, pData points to the message.
However, as we have already seen, this message contains only information about the
application and server, useless for this tutorial. Thus we ignore the message data, and
call another function Process_Connected() that must take care of the connection
completion. All messages will be processed using the callback procedure above. Well add
more case as soon as well need to process other types of messages. All OPEN messages
A call to define our set of data to be returned, another call to send the data request.
Lets go to the details of the data definition. What we see is that we need to maintain a
list of data-define identifiers well use when talking with the server.
We bind the ID with the list of data and units. Then when we want the server to return
the data, we only provide the ID. The server has the definition somewhere in its tables
associated with our client.
// Unique IDs of data definitions hosted by SimConnect
static enum DATA {
DATA_ACFT_POSITION,
....
};
Well add IDs to this enumeration when well need more. Note that we could just use
plain numbers instead of enum, but this would be a little bit difficult to remember them.
Now lets explain to SimConnect which data we are looking for:
// prepare data definitions
void Prepare_Data()
{
// set up the data definitions
printf(\nSending Data definitions.);
SimConnect_AddToDataDefinition(hSimConnect,
DATA_ACFT_POSITION, Plane Altitude, feet);
SimConnect_AddToDataDefinition(hSimConnect,
DATA_ACFT_POSITION, Plane Latitude, degrees);
SimConnect_AddToDataDefinition(hSimConnect,
DATA_ACFT_POSITION, Plane Longitude, degrees);
SimConnect_AddToDataDefinition(hSimConnect,
DATA_ACFT_POSITION, Heading Indicator, degrees);
SimConnect_AddToDataDefinition(hSimConnect,
DATA_ACFT_POSITION, Airspeed True, knots);
}
We provide the handle to our connection hSimConnect, this will be the case for all
functions. SimConnect needs to know who is talking and check we are not unknown
strangers. Lets forget the second parameter for a couple of minutes. We also provide the
ID of the data we want. With that, SimConnect knows we want latitude, longitude, etc
as well as which units to use for the answer. We then provide the ID of an object in the
simulation. When we called Request_User_Position(), we provided the ID of the
users aircraft as a paramater, we are using it here. The last parameter says how many
times we want the data to be returned by SimConnect. As you remember SimConnect is
asynchronous and will not provide the data we want during the call to SimConnect_
RequestDataOnSimObject. Instead it will post a message when ready. In addition
it can post this message at interval. We just need to tell what we want. The different
possibilities are defined in the header file:
// Object Data Request Period values
enum SIMCONNECT_PERIOD {
SIMCONNECT_PERIOD_NEVER,
SIMCONNECT_PERIOD_ONCE,
SIMCONNECT_PERIOD_VISUAL_FRAME,
SIMCONNECT_PERIOD_SIM_FRAME,
SIMCONNECT_PERIOD_SECOND,
};
The difference between visual frame and sim frame is about including or not the
frames that are not rendered. However the documentation is not so clear about that.
Anyway SimConnect will send us a nice message with the info we want. We need to
be reminded of our request at that time, because well have so many requests that we
cant remember for sure... so well provide a number now, and SimConnect will include
in its replies. This mechanism will be needed for every request. So well maintain an
enumeration which is assured to grow up shortly:
// Unique IDs of requests to SimConnect
static enum REQUESTS {
REQUEST_INIT_POSITION,
};
As you can see, this is the second parameter we used in our request.
Now that our request for the users aircraft position has been sent, what do we do? Well,
we cant do anything before receiving the reply. It will come thru a call to our callback
procedure. But we need to handle it. It will have the type SIMCONNECT_RECV_ID_
SIMOBJECT_DATA. So lets just add another case in our MyDispatchProc() routine:
case SIMCONNECT_RECV_ID_SIMOBJECT_DATA:
// An answer to a data request on an object
Process_Data(pData);
break;
So what do we do here? First we cast our message pointer to the type used with a
SIMCONNECT_RECV_ID_SIMOBJECT_DATA message:
SIMCONNECT_RECV_SIMOBJECT_DATA *pObjData =
SIMCONNECT_RECV_SIMOBJECT_DATA *)pData;
We could check it is actually the same object than the one for which we requested data,
but thats assumed here. We may limit our check to the request ID:
switch(pObjData->dwRequestID)
{
case REQUEST_INIT_POSITION:
Should we receive data not related to our own request, it would be ignored. What is of
interest here is the values of the requested aircraft parameters. There is a pointer to them
in dwData. This is a pointer to a structure crafted by SimConnect from our data definition
(DATA_ACFT_POSITION). We need to define somewhere in our program the same data
structure:
// returned after a data request based on DATA_ACFT_POSITION
struct ACFT_PARAM
{
double altitude;
double latitude;
double longitude;
double heading;
double airspeed;
};
and now we can cast the pointer returned to a pointer to this type:
ACFT_PARAM *pS = (ACFT_PARAM*)&pObjData->dwData;
double
double
double
double
AIdistance
AIRelBearing
AIHeight
AIRelSpeed
=
=
=
=
.2;
270;
-50;
0;
//
//
//
//
We are now nearly ready to create this awaited new aircraft. We just need to put the data
needed for the creation into the structure requested by the SimConnect API. (this structure
SIMCONNECT_DATA_INITPOSITION is defined in the API header).
// Prepare data for the creation of a SimConnect AI object
SIMCONNECT_DATA_INITPOSITION Init;
Init.Altitude
= result.alt;
Init.Latitude
= result.lat;
Init.Longitude = result.lon;
Init.Pitch
= 0.0;
Init.Bank
= 0.0;
Init.Heading
= pS->heading;
Init.OnGround
= 0;
Init.Airspeed
= (DWORD)(pS->airspeed + AIRelSpeed);
REQUEST_CREATE_AI);
Container_Title is the name of the AI object to create (i.e. the type of object to
create), while Tail_Number is obviousy the call sign. As usual for us now, we need to
provide a request identifier to SimConnect. Our enumeration for request looks now like
this:
static enum REQUESTS {
REQUEST_INIT_POSITION,
REQUEST_CREATE_AI,
}
How to you find the container title? Another gap in FSX SDK documentation. The list is not
provided, instead you have to look, as explained, into FSX description of objects in your
FSX folders.
If you started coding in C# or any MS managed code, bear in mind that you need to
marshal your data structures, with an additional effort for the strings.
The creation function returns a result that is S_OK (defined in the API header file) or
something else. We just check S_OK to confirm the request was accepted.
We have submitted our creation request, the next step is to receive the notification of its
completion. It will arrive thru the callback procedure and will be tagged with message
type SIMCONNECT_RECV_ID_EVENT_OBJECT_ADDREMOVE. We need to add some
code in our callback procedure.
an object ID. Note that there is no corresponding notifications when these objects are
destructed. This ID assignment notification will help us. However well need also to be
informed of our aircraft removal, because we need to stop sending request to FSX as soon
as our little client has no purpose anymore (in this case maybe we should re-create it? Its
left up to you...).
The AI aircraft should be removed after a collision (actually not) or some other event FSX
would jugde critical (we lack information on that). It will also be removed if it leaves the
reality bubble of the user, that if it is further than something around 100 km. The SDK
documentation talks about that.
It is clear we have to implement the handler for removals. For the same price well have a
handler for additions.
repeat the string in the message, only EventID. That means EventID is another unique
number that will be defined in an enumeration:
// Unique IDs for notified events
static enum EVENTS{
EVENT_ADDED,
EVENT_REMOVED,
};
We need to handle both notifications by adding this code in our callback procedure:
case SIMCONNECT_RECV_ID_EVENT_OBJECT_ADDREMOVE:
// AI object added or removed
Process_Object_Added_Removed(pData);
break;
Somewhere in our code, we need to subscribe to object addition and object removal
events. This has to be before the first creation request is submitted. A valid time is when
we send our data definition (function Prepare_Data). We can add the code to subscribe
here and rename the function:
To know if the notification is for an addition or for a removal, we need to check the
uEventID reported in the message and compare it with the event ID provided for the
subscription to this class of events. We know what was the request for. Lets put that
toghether in the function that well call when notified:
void Prepare_Data_And_Events()
{
// set up the data definitions
...
// subscribe to events
printf(\nSubscribing to events.);
SimConnect_SubscribeToSystemEvent(hSimConnect,
EVENT_ADDED, ObjectAdded);
SimConnect_SubscribeToSystemEvent(hSimConnect,
EVENT_REMOVED, ObjectRemoved);
}
SimConnect_SubscribeToSystemEvent is declared this way:
HRESULT SimConnect_SubscribeToSystemEvent(
HANDLE hSimConnect,
SIMCONNECT_CLIENT_EVENT_ID EventID,
const char* SystemEventName
);
printf(\nRestart me.);
case REQUEST_CREATE_AI:
// Our AI aircraft
printf(\nAI Aircraft created (%d)
pAssObjData->dwObjectID);
AI_Acft_ID = pAssObjData->dwObjectID;
AI_Acft_Valid = true;
// Start a timer
SimConnect_SubscribeToSystemEvent(hSimConnect,
EVENT_TIMER, 4sec);
printf(\nSubscribing to Timer ticks.);
break;
}
break;
}
}
In the code above, we cast the message pointer to the actual structure for this event. Then
we look at uEventID to sort additions and removals based on the event ID provided at
the subscription. For the additions we do nothing except leaving a debug trace. For the
removal, we check if the object removed isnt our aircraft. As we will see in a couple of
minutes, this object ID has been stored in the variable AI_Acft_ID. If the comparaison
confirms the removal, then we reset the flag keep_going that was set when the aircraft
was confirmed as created. We also reset the flag which indicates the AI_Acft_ID
variable contains a valid ID.
You may wonder why we dont use the EVENT_ADDED case to confirm our AI aircraft
was created. The reason is that we cant, because the SIMCONNECT_RECV_EVENT_
OBJECT_ADDREMOVE notification doesnt carry the Request ID that led to the creation.
Without this information, we cannot know if the new object is the one we are expecting...
So we are bound to handle also the notification of the ID assignment.
}
}
Then we subscribe to a timer event sent every 4 seconds. Well use it to send a new
waypoint to the AI aircraft. This way well feed the aircraft with waypoints that keep it in
the vicinity.
SimConnect_SubscribeToSystemEvent(hSimConnect,
EVENT_TIMER, 4sec);
detection later) are all received under the same message type, SIMCONNECT_RECV_ID_
EVENT.
case SIMCONNECT_RECV_ID_EVENT:
// One of the FSX events this client has subscribed to
Process_Event(pData);
break;
We check the message is for EVENT_TIMER. At this time we should request the current
position of the user aircraft. We would then wait for the server reply, and in the handler
of the reply we would create a new waypoint based on the current position of the users
aircraft. This is what well do except that we need to stop creating waypoints when the
simulation is paused or when the AI aircraft has been removed:
if (keep_going && !Paused)
{
// request the new position of the user
Request_User_Position(
SIMCONNECT_OBJECT_ID_USER, false);
}
keep_going will be reset in the handler for object removal notifications, while toogling
Paused needs to be linked to a new event from SimConnect. Well take care of that soon.
Regarding requesting the current position, it would make sense to use the code we wrote
for asking the initial position. However when we are notified of the users position, we
need to know which request is answered to execute the related actions set which are
different. It means we need to have different request IDs, we cannot reuse REQUEST_
INIT_POSITION. We will insert another ID, REQUEST_NEXT_POSITION, in our
enumeration of request IDs. To tell Request_User_Position() which request ID
needs to be used, we use a boolean flag, set only for the initial request. This is our handler,
modified to suit both types of requests:
// Request the position of the users aircraft
void Request_User_Position(
SIMCONNECT_OBJECT_ID SimObject, bool Initial)
{
SimConnect_RequestDataOnSimObject(
hSimConnect,
Initial ?
REQUEST_INIT_POSITION : REQUEST_NEXT_POSITION,
DATA_ACFT_POSITION,
SimObject,
SIMCONNECT_PERIOD_ONCE);
printf(\n%s position requested (%d).,
Initial ? Initial : Current, SimObject);
// (the position will be received later)
}
We mentionned our need to capture pauses so that we dont send useless waypoints to
the AI aircraft when the simulation is paused. The waypoints would be all identical, since
the users aircraft is not moving, and in addition the AI aircraft would receive them all at
the same time, when the simulation is restarted. Being notified of Pause is a matter of
subscribing to the appropriate event and again to handle the notifications. Lets add a
subscription at the same location than the previous ones:
void Prepare_Data_And_Events()
{
// set up the data definitions
...
// subscribe to events
...
HRESULT hr = SimConnect_SubscribeToSystemEvent(
hSimConnect, EVENT_PAUSE_TOGGLE, Pause);
Paused = (hr == 0) ? false : true;
We are subscribing to SimConnect event Pause, and we use the event ID EVENT_
PAUSE_TOGGLE to remember what is this event. (for later, when well be notified and
provided this event ID) This new ID has been added to our EVENTS enumeration (this
is straightforward, not need to show you the updated code). Note that the order of the
symbols in the enum definition has no importance, only their uniqueness is meaningful.
When we subscribe to events like Pause, the call to SimConnect returns the current status
of the parameter. Here it returns 0 if the simulation is currently unpaused. this allow us to
set our Paused flag to the appropriate value immediately.
The notifications are handled this way, reusing the existing event handler already used to
handle EVENT_TIMER:
switch(evt->uEventID)
{
case EVENT_TIMER:
...
case EVENT_PAUSE_TOGGLE:
// the simulation is paused or unpaused
Paused = evt->dwData == 0 ? false : true;
break;
}
We get the new status of the simulation in dwData. We update our Paused flag
accordingly.
case REQUEST_NEXT_POSITION:
// Periodic update of the users position
// We send the coordinates of a waypoint nearby
// But only if the AI aircraft is flying...
if (AI_Acft_Valid && keep_going)
{
printf(\nNew position received.);
{
// WP position and desired speed at the WP
double WPdistance = .5; // NM
double WPRelBearing = 0; // degrees clockwise
double WPHeight = -50; // meters above
double WPRelSpeed = 5; // kts above
// Compute Lat / Lon of the WP
ACFT_POSITION result = Compute_Location(
pS->latitude, pS->longitude,
pS->altitude, WPdistance,
fmod(pS->heading + WPRelBearing, 360),
WPHeight);
// force VS calculation
unsigned long flags =
SIMCONNECT_WAYPOINT_SPEED_REQUESTED |
SIMCONNECT_WAYPOINT_COMPUTE_VERTICAL_SPEED;
// (code to create the WP goes here)
// (see next section)
When we receive the users aircraft position from SimConnect, we check we really need to
create a waypoint:
if (AI_Acft_Valid && keep_going)
We need to create a waypoint when we are notified of the current users position. The
corresponding data request is submitted every 4 sec by the EVENT_TIMER handler. We
already have a handler that processes notifications of data delivery (Process_Data()),
we just need to adapt it so that it can handle replies to REQUEST_NEXT_POSITION
requests in addition to replies to REQUEST_INIT_POSITION requests.
case REQUEST_INIT_POSITION:
...
...
break;
i.e. the AI aircraft creation has taken place (confirmed by AI_Acft_Valid) and the AI
aircraft hasnt been destructed (confirmed by keep_going). The position of the waypoint
will be relative to the position of the users aircraft: distance in NM, relative angle, altitude
difference, speed difference.
// WP position and desired
double WPdistance
= .5;
double WPRelBearing = 0;
double WPHeight
= -50;
double WPRelSpeed
= 5;
speed at the WP
// NM
// degrees clockwise
// meters above
// kts above
Since we need to provide a latitude and longitude rather than a distance and a bearing
for the creation of the waypoint, we call again our spherical routine. We ensure the
absoulte bearing is in the range 0-360 by calling a fmod.
When creating the waypoint, well specify a speed. This is the desired speed for the aircraft
when it reaches the waypoint. Similarly we want FSX simulation to compute whatever
vertical speed is required to reach the altitude at the waypoint. When calling the function
that creates the WP, well need to provide flags that ask for the speed and the vertical
speed to be used. They are ORed:
flags =
SIMCONNECT_WAYPOINT_SPEED_REQUESTED |
SIMCONNECT_WAYPOINT_COMPUTE_VERTICAL_SPEED;
Creating a waypoint
First, we goup the WP data in an array of SIMCONNECT_DATA_WAYPOINT elements. If
we want to send multiple waypoints, then we describe one WP per array element.
// Prepare data for the creation of a WP
SIMCONNECT_DATA_WAYPOINT waypoint[1];
waypoint[0].Latitude = result.lat;
waypoint[0].Longitude = result.lon;
waypoint[0].Altitude = result.alt;
waypoint[0].Flags = flags;
waypoint[0].ktsSpeed = pS->airspeed + WPRelSpeed;
waypoint[0].percentThrottle = 0;
The percentThrottle value is the quantity of power we want for the aircraft when
it reaches the WP. We dont use it here (we should force its use by adding a flag to our
flags value).
To send the WP to the aircraft, well just use the function that sends data to an object.
Sending data to an object requires a data definition to tell SimConnect how to understand
what we are sending. We already defined data, well just add the new definition to the
existing ones.
Thats it. The AI aircraft is now flying towards this WP. It would be nice to visualize it. This
will be the icing on the cake.
As the data definition needs to be assigned a unique ID, well also update our data
definition enumeration to add the symbol DATA_NEXT_POSITION:
Creating a marker
The green vertical arrow or the point of interest objects used in missions would be
perfect markers to visualize the location of a waypoint. However, I dont know how to
use them in a SimConnect application. It seems the only objects we can create are those
found in the SimObjects folder of FSX. So the best I can think of is a parachute.
Like we did for the AI aircraft, we need to tell SimConnect where to create the object. This
will be described in a structure SIMCONNECT_DATA_INITPOSITION.
// Prepare data for a SimConnect AI object
SIMCONNECT_DATA_INITPOSITION Init;
Init.Altitude
= result.alt;
Init.Latitude
= result.lat;
Init.Longitude = result.lon;
Init.Pitch
= 0.0;
Init.Bank
= 0.0;
Init.Heading
= pS->heading;
Init.OnGround
= 0;
Init.Airspeed
= 0;
With this set of data, we can create the little parachute. Well use the SimConnect function
SimConnect_AICreateSimulatedObject which allow us to create objects that
dont fly.
// Create an AI object to mark the position
SimConnect_AICreateSimulatedObject(hSimConnect,
Food_pallet,
Init,
REQUEST_ADD_MARKER);
Note that we used the new request ID REQUEST_ADD_MARKER which will allow us
to detect the creation completion. As usual, the enumeration of request IDs has been
updated.
The name of the container to create is found in the configuration file of the object in FSX
SimObjects folder. There is no list of objects in the SDK help file, only some examples.
If you run this program now, itll work. Ensure you launch it while youre already flying
your aircraft in FSX. If this is not the case, the AI aircraft will be created with a speed
of 0 kt. And maybe below the ground level, depending on the settings you chose for the
position where to create the AI....
Youll see the AI aircraft poping up nearby out of nowhere. And also those magenta
parachutes dropped every four seconds.They will materialize your route on the ground
(actually a route parallel to your route). When we send a single WP to the aircraft, the
previous WPs are cancelled from its flight plan. Only the most recent parachute is actually
materializing an active WP. If you want, you can add some code to your program, to
remove the previous parachute when a new one is created. It should be in the section
of code that is called when we are notified that an ID has been assigned to our last
parachute (Process_Object_ID_Assignment()):
case REQUEST_ADD_MARKER:
// no more than one marker in the simulation
Previous_Marker = Last_Marker;
Last_Marker = pAssObjData->dwObjectID;
if (Previous_Marker != NULL)
{
// remove the previous marker
SimConnect_AIRemoveObject(hSimConnect,
Previous_Marker, REQUEST_REMOVE_MARKER);
}
With that last set of instructions, we have completed the tutorial. The full code
contains non significant additions that you will be able to discover by reading thru
FollowThisPlane.cpp source.
PS: the code used to compute the coordinates of a point based on its distance and
bearing relative to a point with known coordinates is:
const double PI = 3.14159265358979323846;
const double DEG_TO_RAD = 3.14159265358979323846 / 180.0;
const double EARTH_RADIUS_NM = 6371.008 / 1.852;
double lat1 = refLat * DEG_TO_RAD;
double lon1 = refLon * DEG_TO_RAD;
double d_R = dist / EARTH_RADIUS_NM;
double brng = bearing * DEG_TO_RAD;
result.lat = (
asin(sin(lat1)*cos(d_R) +
cos(lat1)*sin(d_R)*cos(brng)))
/ DEG_TO_RAD;
result.lon = (
lon1 +
atan2(
sin(brng)*sin(d_R)*cos(lat1),
cos(d_R)-sin(lat1)*sin(result.lat)))
/ DEG_TO_RAD;
The result is not good, though for the purpose its sufficient. Youll see the effect when
setting the waypoint position at some distance (1/2 NM) and the bearing at 360. The WP
is not created directly ahead, but on a side. Regardless of the bearing set, when your turn
your aircraft, the position of the parachute creation will move related to the heading, it
will change from left to right or right to left.
Ive no clue about whats wrong, if you see where is the problem... send me your tested
code. Thanks.