![]() |
IFD XP Trainer (Distance to wpt and FLT PLN sync) |
Post Reply ![]() |
Author | |
ricardo ![]() Senior Member ![]() Joined: 17 Jan 2022 Location: Seattle, wa Status: Offline Points: 139 |
![]() ![]() ![]() ![]() ![]() Posted: 14 Dec 2024 at 5:18pm |
I was futzing around with the SDK a few months back, wanting to fix 2 things that bug me about the IFD XPlane Trainer.
1 - The distance to next waypoint is not set by the IFD XP, so if you are using a G5 instrument, you dont get the distance to next Waypoint annuciated properly on the instrument. (on the HSI) 2 - It does not have a mode to copy the flight plan to the NAV1 GPS.. - whether its a GNS 430 or other. So i went ahead and used the Avidyne SDK and the XPlane plugin SDK and wrote a plugin to do both of these things. I dont have the proper motivation to compile this plugin and make it available for download (I do personally use it on my own XPlane), so i'll just paste the code that i wrote to do this, in case someone else finds it useful and/or wants to clean it up and make it more widely available. i'd say it mostly works. -- I am using this plugin on my own training with XPlane at home. ====================================================== #include <stdarg.h> #include <stdio.h> #include <string.h> #include <time.h> #include <vector> #include "XPLMProcessing.h" #include "XPLMDataAccess.h" #include "XPLMUtilities.h" #include "XPLMPlugin.h" #include "XPLMNavigation.h" #include "avisdk/Tier1/FlightData/flightData.h" #include "avisdk/Tier1/FlightData/Waypoint.h" #include "avisdk/UtilityClasses/AviCaps.h" #include "avisdk/Tier2/Crc32Bit.h" static XPLMDataRef gEnabledDataRef = NULL; static XPLMDataRef gConnectedDataRef = NULL; static XPLMDataRef gUpdateRateSecDataRef = NULL; static XPLMDataRef gFlightPlanSizeDataRef = NULL; static XPLMDataRef gFlightPlanHashDataRef = NULL; static XPLMDataRef gGpsDmeDistanceNmDataRef = NULL; static XPLMDataRef gOverrideGpsDataRef = NULL; static XPLMDataRef gSyncDistanceToNextWptDataRef = NULL; static XPLMDataRef gDistanceToNextWptDataRef = NULL; static XPLMDataRef gSyncFlightPlanToGPS1DataRef = NULL; static XPLMDataRef gSuspendOverrideGpsDataRef = NULL; static bool gEnabledValue = true; static void SetEnabledDataRefCB(void* inRefCon, int value); static int GetEnabledDataRefCB(void* inRefCon); static bool gConnectedValue = false; static int GetConnectedDataRefCB(void* inRefCon); static float gUpdateRateSecValue = 2.0; static void SetUpdateRateSecDataRefCB(void* inRefCon, float value); static float GetUpdateRateSecDataRefCB(void* inRefCon); static int gFlightPlanSizeValue = 0; static int GetFlightPlanSizeDataRefCB(void* inRefCon); static unsigned int gFlightPlanHashValue = 0; static int GetFlightPlanHashDataRefCB(void* inRefCon); static bool gSyncDistanceToNextWptValue = true; static void SetSyncDistanceToNextWptDataRefCB(void* inRefCon, int value); static int GetSyncDistanceToNextWptDataRefCB(void* inRefCon); static double gDistanceToNextWptValue = 0.0; static double GetDistanceToNextWptDataRefCB(void* inRefCon); static bool gSyncFlightPlanToGPS1Value = true; static void SetSyncFlightPlanToGPS1DataRefCB(void* inRefCon, int value); static int GetSyncFlightPlanToGPS1DataRefCB(void* inRefCon); static bool gSuspendOverrideGpsValue = false; static void SetSuspendOverrideGPSDataRefCB(void* inRefCon, int value); static int GetSuspendOverrideGPSDataRefCB(void* inRefCon); static long gGenerationCounter = 0; static AviSdk::AviCaps ifd; static AviSdk::flightData fpl_monitor; static AviSdk::Waypoint* gWaypoints = nullptr; static float TimedCallback( float inElapsedSinceLastCall, float inElapsedTimeSinceLastFlightLoop, int inCounter, void* inRefcon); typedef unsigned int CRC32; void LOG(const char* format, ...) { char timestamp[16]; time_t ltime; ltime = time(NULL); struct tm* tm; tm = localtime(<ime); sprintf(timestamp, "%04d%02d%02d%02d%02d%02d", tm->tm_year + 1900, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); char message[256]; va_list args; va_start(args, format); vsprintf(message, format, args); va_end(args); XPLMDebugString(timestamp); XPLMDebugString(": "); XPLMDebugString(message); XPLMDebugString("\n"); } CRC32 GetFlightPlanHash(std::vector<AviSdk::Waypoint>& waypoints) { ::AviSdk::Crc32Bit crc; crc.Reset(); for (AviSdk::Waypoint& wp : waypoints) { crc.Add((const unsigned char *)wp.getID(), strlen(wp.getID())); if (wp.isActive()) { crc.Add((const unsigned char *)"active", 6); } } return crc.Value(); } PLUGIN_API int XPluginStart( char * outName, char * outSig, char * outDesc) { strcpy(outName, "AviSync"); strcpy(outSig, "xplanesdk.examples.timedprocessing"); strcpy(outDesc, "A plugin to copy IFD FPL Data to the onboard FMS."); /* Find the data refs we want to record. */ gEnabledDataRef = XPLMRegisterDataAccessor("avidyne/ifd/plugin_enabled", xplmType_Int, 1, GetEnabledDataRefCB, SetEnabledDataRefCB, NULL, NULL, // Float accessors NULL, NULL, // Double accessors NULL, NULL, // Int array accessors NULL, NULL, // Float array accessors NULL, NULL, // Raw data accessors NULL, NULL); gConnectedDataRef = XPLMRegisterDataAccessor("avidyne/ifd/connected", xplmType_Int, 0, GetConnectedDataRefCB, NULL, NULL, NULL, // Float accessors NULL, NULL, // Double accessors NULL, NULL, // Int array accessors NULL, NULL, // Float array accessors NULL, NULL, // Raw data accessors NULL, NULL); gUpdateRateSecDataRef = XPLMRegisterDataAccessor("avidyne/ifd/update_rate_sec", xplmType_Float, 1, NULL, NULL, // Int accessors GetUpdateRateSecDataRefCB, SetUpdateRateSecDataRefCB, NULL, NULL, // Double accessors NULL, NULL, // Int array accessors NULL, NULL, // Float array accessors NULL, NULL, // Raw data accessors NULL, NULL); gFlightPlanSizeDataRef = XPLMRegisterDataAccessor("avidyne/ifd/flight_plan_size", xplmType_Int, 0, GetFlightPlanSizeDataRefCB, NULL, NULL, NULL, // Float accessors NULL, NULL, // Double accessors NULL, NULL, // Int array accessors NULL, NULL, // Float array accessors NULL, NULL, // Raw data accessors NULL, NULL); gFlightPlanHashDataRef = XPLMRegisterDataAccessor("avidyne/ifd/flight_plan_hash", xplmType_Int, 0, GetFlightPlanHashDataRefCB, NULL, NULL, NULL, // Float accessors NULL, NULL, // Double accessors NULL, NULL, // Int array accessors NULL, NULL, // Float array accessors NULL, NULL, // Raw data accessors NULL, NULL); gSyncDistanceToNextWptDataRef = XPLMRegisterDataAccessor("avidyne/ifd/sync_distance_to_next_wpt", xplmType_Int, 1, GetSyncDistanceToNextWptDataRefCB, SetSyncDistanceToNextWptDataRefCB, NULL, NULL, // Float accessors NULL, NULL, // Double accessors NULL, NULL, // Int array accessors NULL, NULL, // Float array accessors NULL, NULL, // Raw data accessors NULL, NULL); gDistanceToNextWptDataRef = XPLMRegisterDataAccessor("avidyne/ifd/distance_to_next_wpt", xplmType_Float, 0, NULL, NULL, // Int accessors NULL, NULL, // Float accessors GetDistanceToNextWptDataRefCB, NULL, // Double accessors NULL, NULL, // Int array accessors NULL, NULL, // Float array accessors NULL, NULL, // Raw data accessors NULL, NULL); gSyncFlightPlanToGPS1DataRef = XPLMRegisterDataAccessor("avidyne/ifd/sync_flight_plan_to_gps_1", xplmType_Int, 1, GetSyncFlightPlanToGPS1DataRefCB, SetSyncFlightPlanToGPS1DataRefCB, NULL, NULL, // Float accessors NULL, NULL, // Double accessors NULL, NULL, // Int array accessors NULL, NULL, // Float array accessors NULL, NULL, // Raw data accessors NULL, NULL); gSuspendOverrideGpsDataRef = XPLMRegisterDataAccessor("avidyne/ifd/suspend_override_gps", xplmType_Int, 1, GetSuspendOverrideGPSDataRefCB, SetSuspendOverrideGPSDataRefCB, NULL, NULL, // Float accessors NULL, NULL, // Double accessors NULL, NULL, // Int array accessors NULL, NULL, // Float array accessors NULL, NULL, // Raw data accessors NULL, NULL); gGpsDmeDistanceNmDataRef = XPLMFindDataRef("sim/cockpit2/radios/indicators/gps_dme_distance_nm"); gOverrideGpsDataRef = XPLMFindDataRef("sim/operation/override/override_gps"); XPLMRegisterFlightLoopCallback(TimedCallback, 1.0, NULL); return 1; } PLUGIN_API void XPluginStop(void) { XPLMUnregisterDataAccessor(gEnabledDataRef); XPLMUnregisterDataAccessor(gConnectedDataRef); XPLMUnregisterDataAccessor(gUpdateRateSecDataRef); XPLMUnregisterDataAccessor(gFlightPlanSizeDataRef); XPLMUnregisterDataAccessor(gFlightPlanHashDataRef); XPLMUnregisterDataAccessor(gSyncDistanceToNextWptDataRef); XPLMUnregisterDataAccessor(gDistanceToNextWptDataRef); XPLMUnregisterDataAccessor(gSyncFlightPlanToGPS1DataRef); XPLMUnregisterDataAccessor(gSuspendOverrideGpsDataRef); XPLMUnregisterFlightLoopCallback(TimedCallback, NULL); } PLUGIN_API void XPluginDisable(void) { } PLUGIN_API int XPluginEnable(void) { return 1; } PLUGIN_API void XPluginReceiveMessage( XPLMPluginID inFromWho, int inMessage, void * inParam) { } void SetEnabledDataRefCB(void* inRefCon, int value) { gEnabledValue = value; } int GetEnabledDataRefCB(void* inRefCon) { return gEnabledValue; } int GetConnectedDataRefCB(void* inRefCon) { return gConnectedValue; } void SetUpdateRateSecDataRefCB(void* inRefCon, float value) { gUpdateRateSecValue = value; } float GetUpdateRateSecDataRefCB(void* inRefCon) { return gUpdateRateSecValue; } int GetFlightPlanSizeDataRefCB(void* inRefCon) { return gFlightPlanSizeValue; } int GetFlightPlanHashDataRefCB(void* inRefCon) { return gFlightPlanHashValue; } static void SetSyncDistanceToNextWptDataRefCB(void* inRefCon, int value) { gSyncDistanceToNextWptValue = value; } static int GetSyncDistanceToNextWptDataRefCB(void* inRefCon) { return gSyncDistanceToNextWptValue; } static double GetDistanceToNextWptDataRefCB(void* inRefCon) { return gDistanceToNextWptValue; } static void SetSyncFlightPlanToGPS1DataRefCB(void* inRefCon, int value) { gSyncFlightPlanToGPS1Value = value; gFlightPlanHashValue = 0; } static int GetSyncFlightPlanToGPS1DataRefCB(void* inRefCon) { return gSyncFlightPlanToGPS1Value; } static void SetSuspendOverrideGPSDataRefCB(void* inRefCon, int value) { gSuspendOverrideGpsValue = value; } static int GetSuspendOverrideGPSDataRefCB(void* inRefCon) { return gSuspendOverrideGpsValue; } float TimedCallback( float inElapsedSinceLastCall, float inElapsedTimeSinceLastFlightLoop, int inCounter, void* inRefcon) { // LOG("TimedCallback(): Enter"); // LOG("TimedCallback(): Counter: %d", inCounter); if (!gEnabledValue) { // LOG("TimedCallback(): Plugin not enabled."); return 5.0; } if (gConnectedValue) { if (gSyncFlightPlanToGPS1Value && gSuspendOverrideGpsValue) { gGenerationCounter += 1; } // LOG("TimedCallback(): Already connected to IFD"); if (fpl_monitor.Update()) { // LOG("TimedCallback(): Update from IFD received"); if (gSyncDistanceToNextWptValue) { gDistanceToNextWptValue = fpl_monitor.get_DistanceToWpt(); XPLMSetDataf(gGpsDmeDistanceNmDataRef, gDistanceToNextWptValue); // LOG("TimedCallback(): Distance to next wpt: %f", gDistanceToNextWptValue); } if (gSyncFlightPlanToGPS1Value) { std::vector<AviSdk::Waypoint> waypoints = fpl_monitor.get_FlightPlan(); CRC32 hash = GetFlightPlanHash(waypoints); if (gFlightPlanHashValue != hash) { gFlightPlanSizeValue = waypoints.size(); gFlightPlanHashValue = hash; // LOG("TimedCallback(): Found an IFD FP with %d waypoints", waypoints.size()); int count = XPLMCountFMSEntries(); for (int index = 0; index < count; index++) { XPLMClearFMSEntry(index); } for (AviSdk::Waypoint& wp : waypoints) { // LOG("TimedCallback(): WP: %d ID:%s LAT:%f LON:%f %s", // wp.getSequence(), wp.getID(), wp.getWayLat(), // wp.getWayLon(), wp.isActive() ? "ACTIVE" : ""); float lat = wp.getWayLat(); float lon = wp.getWayLon(); XPLMNavRef nav_ref = XPLM_NAV_NOT_FOUND; std::vector< XPLMNavType> nav_types{ xplm_Nav_Airport, xplm_Nav_NDB, xplm_Nav_VOR, xplm_Nav_ILS, xplm_Nav_Localizer, xplm_Nav_GlideSlope, xplm_Nav_OuterMarker, xplm_Nav_MiddleMarker, xplm_Nav_InnerMarker, xplm_Nav_Fix, xplm_Nav_DME }; for (XPLMNavType nav_type : nav_types) { nav_ref = XPLMFindNavAid(NULL, wp.getID(), &lat, &lon, NULL, nav_type); if (nav_ref != XPLM_NAV_NOT_FOUND) { char nav_id[256]; XPLMGetNavAidInfo(nav_ref, NULL, NULL, NULL, NULL, NULL, NULL, nav_id, NULL, NULL); if (strcmp(wp.getID(), nav_id) != 0) { // LOG("TimedCallback(): Nav overriden to NOT FOUND"); nav_ref = XPLM_NAV_NOT_FOUND; continue; } break; } // LOG("TimedCallback(): WP is not a nav aid of type: %d", nav_type); } if (nav_ref == XPLM_NAV_NOT_FOUND) { // LOG("TimedCallback(): WP not found in database"); XPLMSetFMSEntryLatLon(wp.getSequence() - 1, wp.getWayLat(), wp.getWayLon(), 1000); // LOG("TimedCallback(): WP added as LAT LON"); } else { XPLMSetFMSEntryInfo(wp.getSequence() - 1, nav_ref, 0); // LOG("TimedCallback(): WP added to FMS"); } if (wp.isActive()) { XPLMSetDestinationFMSEntry(wp.getSequence() - 1); // LOG("TimedCallback(): WP set as active on FMS"); } } } } } else { // LOG("TimedCallback(): No new data from IFD"); } // Disable the GPS override for 1 cycle, every 10 cycles. // This was an experiment I was doing to see if i can get the GNS430 map to update properly // even when the Avidyne XPlane Trainer has taken over the GPS -- half baked at this point. if (gSyncFlightPlanToGPS1Value && gSuspendOverrideGpsValue) { if ((gGenerationCounter % 10) == 0) { XPLMSetDatai(gOverrideGpsDataRef, 0); } else if (XPLMGetDatai(gOverrideGpsDataRef) == 0) { XPLMSetDatai(gOverrideGpsDataRef, 1); } } } else { LOG("TimedCallback(): Looking for IFD..."); gConnectedValue = ifd.Update() && ifd.IsConnected(); if (gConnectedValue && !ifd.IsSDKVerOK()) { LOG("TimedCallback(): SDK Version mismatch"); LOG("TimedCallback(): Exit Abnormal"); return 0; } if (gConnectedValue) { LOG("TimedCallback(): IFD Found"); fpl_monitor.Init(ifd.GetIpAddr()); LOG("TimedCallback(): FPL Monitor Initialized"); } } /* Call us back in a few seconds */ // gUpdateRateMillisValue = gConnectedValue ? fpl_monitor.GetUpdateRate() : ifd.GetUpdateRate(); // gUpdateRateMillisValue = gUpdateRateMillisValue < 1000.0 ? 1000.0 : gUpdateRateMillisValue; // LOG("TimedCallback(): Requesting callback in %f seconds", gUpdateRateSecValue); // LOG("TimedCallback(): Exit Normal"); return gUpdateRateSecValue; } ======================================================
Edited by ricardo - 14 Dec 2024 at 5:20pm |
|
![]() |
|
Melohn ![]() Senior Member ![]() ![]() Joined: 11 Dec 2013 Location: PHNL Status: Offline Points: 150 |
![]() ![]() ![]() ![]() ![]() |
It would be great if you would consider also posting the compiled plugin for X-plane users to use. Windows version would be most helpful. Only a small number of people have the SDK.
I’m also interested in your “half baked” idea to pause control of the sim to allow for example the ID function on the IFD Trainer to identify the tuned navaid; this is very useful when simulating an ILS or VOR approach. It’s easy enough to remember to switch the Nav source to VLOC, but switching back on a missed approach works less than half of the time, at least for me. Edited by Melohn - 14 Dec 2024 at 8:20pm |
|
![]() |
|
ricardo ![]() Senior Member ![]() Joined: 17 Jan 2022 Location: Seattle, wa Status: Offline Points: 139 |
![]() ![]() ![]() ![]() ![]() |
The thing that keeps me from doing that is that i never built an UI for the plugin, its all controlled by xplane data refs .. which is rather not user-friendly. (i did put together an air manager UI that i use to control it, but that is even more confusing for the average pilot imo) ..
|
|
![]() |
Post Reply ![]() |
|
Tweet
|
Forum Jump | Forum Permissions ![]() You cannot post new topics in this forum You cannot reply to topics in this forum You cannot delete your posts in this forum You cannot edit your posts in this forum You cannot create polls in this forum You cannot vote in polls in this forum |