IPv6 … isn’t!
Yesterday we shipped the 1.3 update for the CRJ. It included the much-awaited remote CDU functionality, which is basically a web server running in the plugin, serving the CDU screen in HTML format via Ajax.
The webserver in the plugin is a very basic HTTP server built with boost.asio, an incredibly powerful cross-platform asynchronous network library.
Thinking progressive as I always do, I of course built the webserver with IPv6 support, and configured it to always start in dual-stack mode, that means it serves the CDU both via IPv4 and IPv6.
I thought I could relax on that basis, and not touch this code again until the IPv6 address space is full.
I was so wrong!
Reports from customers kept coming in, that they couldn't start the CRJ any more. A very helpful customer showed me a gdb stacktrace of what was wrong. Guess what: it couldn't open a listening socket on 0::0, the "any" IP address in IPv6.
No, this guy was not using Windows 98. He was using the latest and greatest Ubuntu Linux. SRSLY, WTF?! It is 2011 and Ubuntu 11.10 has IPv6 support disabled by default?
Next customer with this problem was using Windows XP. Still the most popular MS OS these days. Guess what, no IPv6 enabled by default!
After instructing those guys how to enable IPv6 on their machines, I coded a patch for the server, to fallback to IPv4 only when dual-stack is unavailable. This is released as the 1.3.1 hotfix now.
I wonder how this planet is going to make the transition to IPv6 any time soon, when about 25% of my customers just can't use it without browsing through cryptic knowledgebase articles...
Fun with clang or: How to build Qt 4.8 with clang 3 without going insane
Given that XCode 4 on Mac ships with the the clang frontend for LLVM now, I wanted to give this new compiler a try and see if it can replace gcc for me.
To compile the CRJ plugin, I need Qt of course, and given this entry in the Qt blogs I thought it would be fairly easy to get this to work.
I was wrong.
First try: I checked out the Qt 4.8 branch from gitorious, and configured with -platform unsupported/macx-clang but I soon ran into a compile error complaining about an undeclared identifier "SetRect".
Second try: I checked out the specific git revision mentioned in the blog post, and re-configured. Build stops with the same error.
Thrid try: Given that the most recent clang is the 3.0 branch and that Apple ships 2.1, I decided to get myself a more recent clang. I checked out the llvm sources via svn, then the clang sources in the appropriate subdirectory, configured for a release-optimzed build (to boldly go...) and make'd. Some 10 or 20 minutes later I had a clang 3.0. I edited the mkspecs/common/clang.conf to point to my freshly compiled clang in /usr/local/bin and reconfigured Qt (I checked out master/HEAD before). Now the built took longer before it stopped, this time everything of QtCore compiled, but when linking, I got an "undefined symbol for architecture i386: ___eprintf". Oh well.
Fourth try: Google pointed me to this thread which indicated I must built the compiler runtime also. So I did another svn checkout (compiler-rt into llvm/projects/), make clean'd, re-configured and built llvm/clang/compiler-rt again. The built aborted mocking about some missing headers for ARM.
Fifth try: I reconfigured llvm with --enable-architecture=x86 instead of the default --enable-architecture=all, which would built all kinds of backend. The next built took somewhat longer and perhaps 30 minutes later I was ready to try again: with the freshly installed clang I went ahead to compile Qt again. This time it actually linked QtCore, but when compiling QtGui it stopped again with a compiler error:
src/gui/kernel/qt_cocoa_helpers_mac_p.h:218:10: error: cannot initialize return object of type 'NSString *' with an rvalue of type 'const NSString *'
Apparently, clang doesn't like Cocoa.
Sixth try: I make confclean'd and re-configured Qt to build with -carbon. Unfortunately, the next make stopped at exactly the same location, at the Cocoa-helpers. Apparently this header is pulled in every time regardless whether we actually use Cocoa or not.
At this point, I decided to give up on OSX and try my luck on Linux.
I svn co'd llvm, clang and compiler-rt to my Suse, configured for optimzed built and started make. Short time after that *KABOOOM!*. No, not a compile error. GCC crashed. My compiler trapped during compiling. Seriously, WTF? I was first confused by the print output of GCC, that referred to a source file that I wasn't compiling at all, until it occurred to me that the indicated source was a source of GCC, a GCC function that crashed.
Oh the humanity! I went to the #clang channel on irc.freenode.net and luckily some helpful soul indicated what I should do. GCC would break regularly during optimized clang compiles. The way to go would be to build clang with clang
- First, build clang in debug mode without optimizations with gcc
- Then, use the un-optimized clang to build an optimized clang
Luckicly, this guy was spot on. I had encountered this error that prevents GCC 4.5.0 to build clang with -ftree-vectorize. So the non-optimized built succeeded, and then I was able to bootstrap the optimized clang from it.
I checked out Qt master again and configured with -platform unsupported/linux-clang. Build was uneventful, and my minimal testing-Qt (without webkit, Javascript, QtScript, QDeclarative, etc...) was ready within some 30 or 45 minutes.
Configuring Qt-Creator 2.3 to recognize clang as compiler and display its warnings was straightforward.
Now I took a deep breath and compiled my airnav library on -Wall, which is a lot more pedantic than the -Wall of gcc. And bingo!!
The -Wall -Wextra compile however showed some minor warnings that I've never seen with gcc - it complained about an explicit template instantiation outside the namespace of the template. This was fixed changing two lines of code, and after that I got it through -Werror -Wall -Wextra. W00t!
Lessons learned:
- clang is not yet ready to be used on a regular day-to-day basis and still requires lots of fiddling
- Qt's codebase is relatively clean, as it compiles with clang with relatively few warnings
- the Cocoa headers of the 10.7 SDK however are not
- It is great to have yet another reference compiler to check for code-cleanliness
So I reached at least one of my goals: I have another reference compiler I can test my code against: I regularly compile my whole codebase with Visual C++ 10 and gcc 4. Now I have another testing point that reveals more potential issues (portability, unsafe assumptions).
I won't ship clang-compiled plugins to end-users soon, but I will definitely use clang-build plugins myself now for testing purposes.
“Please let me talk to a puke”
(Inspired by "Geben Sie mir bitte einen Kotzbrocken!" by vowe)
There are two types of tech support guys:
-the kind, well-behaved, polite and zealous guy who has absolutely no clue and is following his script
-the grumpy, impolite, nearly-autistic nerd who lacks basic communication skills but knows his stuff
I prefer being handled by the latter. Because he is the only one who can actually solve my problem.
To achieve this, I usually start each call or mail to tech-support by such a mass of geek-level detail that I get escalated to the "puke department" a.k.a. "second level support" fast.
So I considered myself safe when filling out the bug report form for X-Plane with a well-crafted stacktrace from the GNU Debugger, showing the source of a memory error in a Freetype component on OSX, showing up at every X-Plane startup.
I would be the happiest person on earth if I had only ONE customer being capable of using a debugger!
But what happened instead, is that I was trapped for the exchange of FOUR(!!!) additional emails before this apparently got passed on to someone who can actually read debugger output (Austin or Ben one presumes).
Tech supporters of the world:
Please, stop wasting your (and MY!!!) time. If you get a mail from a nerd, pass it to a nerd. AND STOP BEING POLITE TO ME! I want a puke to talk to!
Update Time!
With the .1 update out, I considered it safe to make the move to Lion.
Upgrade itself was smooth and uneventful, but I have a few complaints:
- I downloaded from the AppStore 3 days after 10.7.1 was out, but it installed 10.7. I had to get the 10.7.1 update separately. Don't they update the installer at all?
- Why the **** did they make the Dashboard a separate space? Luckily you can change this in the "Mission Control" settings.
- It is sloooooow. Going fullscreen is not smooth. Switching spaces is not smooth. Snow Leopard feels fast as hell. The Lion feels more like a Lion on Valium. Or like this one.
Other than that, transition was great. XCode 4 was installed in no time, preserving my old XCode3 installation, however I doubt I will ever start the IDE. I only use the command line tools, in combination with QtCreator.
I recompiled all my stuff with llvm-gcc and I look forward to exploring clang. Using the XCode4 tools however means dropping the support for the 10.5 SDK, which means the next CRJ will only run on 10.6+.
Whether to sell on X-Plane.org or X-Aviation (UPDATED!)
As a developer who sells his product on both platforms, I want to give other devs insight on the pros and cons of each platform. Don't expect a definite "winner" or a special recommendation for you, I'm just sharing my personal experience and thoughts here.
Own website vs established shop
First, let's talk about using one of these platforms in the first place. As a newcomer, using a renowned shop platform is a must. You may think your product is good enough to be sold through your personal website only, you can do marketing and advertisement yourself, and you are trustworthy enough so people send you money.
Trust me, you are most certainly wrong!
Buying immaterial things like software online relies on trust, perhaps even more than buying things in real life.
For the customer, buying on your personal website with Paypal button is like buying a used car from "Honest Achmed" in a shabby backyard, whereas buying on X-Aviation or X-Plane.org is more like going into a big Mercedes-Benz showroom and being served by a besuited official.
Sure, people like Peter Hager may be successful selling their planes through their own website, but few people in our community have earned the trust that every product of theirs is top-notch quality.
So, unless you have a track record like Peter, you should rely on the trust that people have in the big online shops, with the thousands of customers already, various payment methods, buzzing support forums, and the strong feeling that you actually get something for your money.
Now that we have decided we must sell through an established selling platform, being X-Plane developers, our choice boils down to the above mentioned "Big Two".
Shop websites
First, the similarities: Both offer a top-notch e-commerce system with product categories, preview pages, shopping cart/checkout, customer database, the whole enchilada. Both offer your customers the option to checkout either with credit card or Paypal.
Considering the overall visual appearance, the X-Aviation shop website wins. It looks tidier and more "Web 2.0"-like with the customer reviews built in. Also the graphics look sleek and modern, the animation is smooth and unobtrusive.
X-Plane.org on the other hand is more like Amazon or the classic GMail. Rather basic in the visual appearance, but well structured and clear. The icons look a bit 1999 though.
Support forums
Also, both offer hosting the support forum for your product - here X-Plane.org is the clear winner. The "Invision Power" forum software they use is simply great. The moderation options are very intuitive to use. Posting multiple attachments and graphics is done easily. The search function is a bit strange though, as you have to be proficient in using the advanced search options to get good results. The quick search tries to be smart but fails often. Another big plus of the IPBoard software is the mobile version. Browsing the X-Plane.org forums on your IPhone or Android is a great experience and feels like a native app. Browsing the X-Pilot.com forums on a mobile device is a pain, as you are constantly scrolling right and left.
The X-Aviation support forum (X-Pilot.com) is a rather simple forum software that is also less intuitive to moderate.
Update 2011-08-25: X-Aviation also uses IPBoard now, which is a huge step forward in usability of their online community. It is a newer software version than the one X-Plane.org uses. However, I've not yet got my moderator rights back which got lost in the migration process. So I can't comment on the moderation options of the latest IPBoard software. The mobile version however doesn't work at all for me. I tapped the link "mobile version" several times, but still get the standard layout on my Android. I heard from IPhone users that it works great there. I messed up more than one support thread when trying to split parallel discussions in a topic apart and literally spent half an hour getting a thread back together.
A big plus for X-Aviation though is that given their smaller portfolio they are able to provide support themselves, as they know all the products they sell a lot better than it would be possible for X-Plane.org with a total of 435 products at the time of writing.
I have complete confidence that when I'm unable to check the forum myself for a few days, Cameron will take care of all the obvious newbie questions, answer all installation-related issues and leave only the really hard-core questions for me to answer. That is something I can't and won't expect from the X-Plane.org store.
So much for the visible, the "customer" side. On the developer side there are huge differences.
Distribution System and Reporting Software
To put it in one sentence: X-Aviation offers a whole distribution system, X-Plane.org offers only a shop.
Part of the excellent distribution system of X-Aviation is a GTK-based wrapper/installer that works the same on Windows, Linux and Mac. It is very user-friendly and goes hand-in-hand with the activation/copy-protection system. I know very little about the technical details, but I know for sure that this system has an unprecedented track record - I have not been able to find a working copy of one of the X-Aviation bestsellers on any of the usual suspect websites. Sure, every system can be broken, but I think X-Aviation set the bar very high on this. If anything can be improved in the installer, then it would be a detection for the folder the user selected. If it tested for the presence of X-Plane.app or X-Plane.exe to make sure the user selected the X-Plane main folder, it would score 100% on my personal scale. So it's only 99%.
Getting all this to work requieres you only to link a ludicrously small static library to your plugin code. This static library is available for all platforms with the gcc compiler, and now -because of my demand- for Windows also with the MS Visual C++ 9.0 compiler.
When sales begin, you can watch them live on the X-Aviation reporting system. The reporting screen is very intuitive and clear, you get the overview over the total sales and earnings on each day, summaries for each month, and a fancy little graph plotting your sales over time, which is extremely helpful if you want to see the impact of marketing like a paper advert. X-Aviation pays out every two weeks, and you may choose to be wired the money to your bank account or to use PayPal.
Compared to that, the X-Plane.org store is rather basic. It's more or less a way you can sell files. They don't provide an installer, a distribution system, or any means of activation or copy-protection. For many X-Plane products this is sufficient, as the typical X-Plane aircraft consists of a single folder you drop into one of the Aircraft/ subdirectories, and you're done. But as soon as you have to distribute other stuff, like the AIRAC data or additional frameworks your plugin requires, giving only a .zip-file to your customers is no longer an option. I resorted to using InnoSetup on Windows and InstallJammer on Linux. Both are very good solutions, easy to configure and the installers look instantly familiar to Windows users. The InstallJammer wizard panes are based on Tcl/Tk and try to resemble Windows Installer, which looks a bit awkward on Linux. For Mac I use Apple Package Maker, which is a huge pain in the ### to configure. Dear Apple, aren't you the company claiming to have the most intuitive UIs? Then go back to the drawing boards with Package Maker, will you?!
Also, it is left up to you, the developer, to think of a way to protect your software against piracy. The X-Plane.org shop offers a system to distribute serial numbers with each purchase though. You can provide a list of pre-computed serial numbers to the shop, that are sequentially issued with the purchases.
The reporting system of the X-Plane.org store also offers a live view and a month-summary, but it lacks many of the nice computations the X-Aviation system does for you, like the earnings-per-month and the neat graph.
You may also choose to be notified of each single order via e-mail. Which I diverted to a separate account, because it totally clutters up my workflow. Payout is at the beginning of each month, preferred payout method is also PayPal.
User base and marketing
Now on to marketing. First thing to note is that X-Plane.org has a HUGE user base. Based on the registered users, the X-Plane.org newsletter is read by nearly ten times the people who read the X-Aviation newsletter. A single marketing action on X-Plane.org can have huge impact. Also, X-Plane.org often has special offers bundling two or three products to be sold at a discounted price. With more than 170.000 registered users, announcements on the X-Plane.org forums reach a lot more readers than on X-Aviation.
Update 2010-08-25: It is also noteable that X-Plane.org ist the 3rd largest Flight sim site behind Avsim and flightsim.com (which are mostly FSX).
Also according to Nicolas' statistics, users spend more time and view more pages per visit than any other flight sim website. Since Nicolas also runs the Avsim store, I trust him he has access to these statistics and knows how to read them.
All things considered, I have to conclude that you can reach more potential customers when selling on X-Plane.org.
X-Aviation in turn is more like a place where they sell hand-picked jewelry. Less users, less potential customers. But customers that are used to very high quality standard. Probably more potent customers, looking for top-notch software. Your product has to be good to be sold on X-Aviation. If the quality of the X-Aviation users makes up for the lack of quantity I don't know yet.
Summary
X-Aviation: Top-notch, state-of-the-art distribution system
X-Plane.org: MASSIVE user base
No, there's no final recommendation. I'm happy to have my product sold on both platforms. I like Cameron as a business partner as much as I like Nicolas, and I don't care that they don't like each other. Perhaps it's "the mercy of late birth", as a former German chancellor put it, that I never engaged in the turf war between them, because the split happened long before I had my first encounter with X-Plane.
So I encourage any author of X-Plane software to find his/hers matching business partner and continue creating and selling great software!
PS: Unlike to my other posts, I allow comments on this. But when spamming gets too worse, I will eventually close them.
Kernelpanic
Yesterday was the first time I saw a Kernel Panic on Mac OSX.
I wanted to play "The Rise and Fall of Ruby Woo" which I had ripped from a CD to my Notebook a long time ago and now transferred from my backup HD.
So I selected the 12 tracks in the folder and double-clicked to open, expecting it would launch iTunes and create a Playlist, or something like that. What I didn't realize was that the tracks were .ogg files, not mp3s (I had ripped them with Rythmbox). Apparently, Google Chrome is the application of choice to play ogg files on my system.
So Chrome popped up, opening 12 new tabs, in each one starting a track, all at the same time.
When closing the tabs, suddenly all the chaotic sound got stuck in a loop and a grey window appeared that I never saw before:
Reboot was uneventful and my uptime was already gone with the 10.6.8 update yesterday. I had DiskUtility check for errors though. Luckily, no problems.
snprintf is not _snprintf_s_WTF_SNAFU !
The last three days were filled up with collecting all the customer reports and preparing the first service pack for the CRJ. Luckily, no real showstopper showed up, but one thing was strange:
I was fixing the display of 25kHz spaced COM-frequencies, where the CRJ 1.0.0 software had a stupid rounding error. I implemented the fix on my Linux Notebook, and was satisfied with the result. I pushed the fix to my git repository, pulled on my Windows PC and compiled again. Starting up the CRJ I had an instant crash with the dreaded "X-Plane.exe has stopped working"-window.
What had happened? To display the value on the small RTU display, I use a well-known C99 function called "snprintf()". It is the safe counterpart of sprintf() (Stream print formatted) that takes an additional argument, the maximum number of characters to be written to the destination buffer. What is great about snprintf() is that it guarantees the output buffer is null-terminated, even if when the output is truncated. And that was precisely what I wanted: The output had to be truncated to 7 characters, 3 characters the frequency before the decimal, then the decimal point, then two digits after the decimal, then the null-terminator. If I put in the frequency as 136.375 it should be truncated so the string reads 136.37\0
Fair enough, now why was the CRJ crashing so violently on Windows? The answer is: Visual C++ doesn't have an snprintf() function. There exist the following two "equivalents": _snprintf() and sprintf_s(). The former was what I used - and guess what - the Microsoft _snprintf() does NOT guarantee the null-terminator!!1!
So the frequency on the RTU read something like 136.375!$Dg=73*+#?§ with some random garbage behind the 7th character.
snprintf() is a C99 standardized function, for god's sake! It's not a GNU extension or a Torvalds memorial function. It's flipping C99 AND C++0x!
Google for the rescue! Apparently quite a lot of people on MSDN have already stumbled over the retarded _snprintf function and there is a workaround that behaves consistent to the C99 requirements:
#ifdef _MSC_VER #include <cstdarg> inline int my_snprintf(char* str, size_t size, const char* format, ...) { size_t count; va_list ap; va_start(ap, format); count = _vscprintf(format, ap); _vsnprintf_s(str, size, _TRUNCATE, format, ap); va_end(ap); return count; } #define snprintf my_snprintf #endif
Now that's a kludgy workaround that keeps the CRJ flying...
Analyzing how X-Plane propagates your position
No, this is not a "it's been a while"-post. You probably all know what I've been working on.
Instead, this is a "fancy engineering stuff" post.
Let's discuss briefly how X-Plane knows where your planes is. X-Plane, as we all know, calculates the forces that act on your airplane. The force that the engine applies to your airframe, for example. When you think back to your 9th grade physics class, you will recall that force is mass times acceleration. Or in other words, if you assume the mass of the airplane constant, force is a direct measure for the acceleration of your airplane.
Now recall that when you integrate acceleration over time, you get the speed. So by knowing the forces that X-Plane calculates based on the effect of the air on your plane, X-Plane knows the acceleration on your plane, and if you step forward a little discrete timestep ("one frame") and integrate this acceleration over time, X-Plane knows the speed of your plane.
Position (or, more accurately, distance) is velocity integrated over time. So when you progress another little discrete timestep and integrate the speed, you get the distance the plane has travelled. Add this distance to the point where your flight started and voila, X-Plane knows where your plane is now.
Okay, why do I care about this? When developing plugins for simulating, say, an FMC, I come across the need to know several parameters all the time, and these are, among others, position, groundspeed and true track (in X-Plane's jargon called "hpath"). The question I was asking myself was "I can get this out of the datarefs, of course. But could I possibly get this data like the real units do, from accelerometers and gyros?"
So what I tried to do is: Get the position out of X-Plane using ONLY a simulated accelerometer. The accelerometer is easy: X-Plane provides three datarefs to get the three local accelerations (sim/flightmodel/position/local_ax, .../local_ay, .../local_az).
So starting with nothing but an initial position and heading, I set up a flightloop callback that is called every frame, and in each callback I did what I know X-Plane must also do: Integrate the local accelerations twice and propagate from the last known position.
The first approach was like this:
- get speed by explicit Euler integration of the acceleration
- get distance by another explicit Euler integration of the speed
- calculate the Place/Bearing/Distance point from the last known location (in Lat/Lon) by using the last known hpath and the distance I just calculated
- Set this PBD point as the new position and start over
Now this proved to be a bad idea: Position of X-Plane and my calculated position diverged rather quickly. After a minute of flight, I was more than a mile off.
I decided to scratch all the fancy great circle maths (needed for the Lat/Lon calculation), and stay in X-Plane's OpenGL coordinate system as long as I could.
My new approach was calculating the position entirely in this coordinate system and use XPLMLocalToWorld() only as a finalizer when displaying the coordinates.
Much better! I was down to like a mile per 10 minutes of flight. Completely useless if you want to cross the pond based on this navigation, but hey, I'm learning here!
Now I began messing with the integration methods. Explicit Euler is a very rough approximation. I briefly thought about using some pre-calculations and using a Runge-Kutta integration, but then I thought I could just save the last acceleration data in a list and use a multistep method. Adams-Bashfort is the most straightforward to use.
So now I had my setup:
- push the acceleration into the list of acceleration history (and drop the oldest out of the history)
- get speed by an Adams-Bashfort integration of the acceleration history
- push this speed into the speed history and drop the oldest speed
- do another Adams-Bashfort integration, this time on the speed history, which gives the travelled distance
- add the travelled distanced onto the local OpenGL coordinates
- transform to world coordinates and write those to datarefs
The result is w00t! I flew the little Cirrus jet in bumpy weather for like an hour, before calculating the difference between my position and X-Plane's. The difference was less than 500 meters. Now this is a navigation system you can fly cross-the-pond with!
So what is all this about? It's the proof that you can simulate navigation in X-Plane WITHOUT using all the data that X-Plane gives you, but you don't have in real life applications. An FMS in the real world can't ask the plane for the groundspeed (unless you have a GPS receiver, of course), it can only ask accelerometers, laser gyros, air data sensors, etc.
What my little experiment shows, is that you can do this in X-Plane and simulate an Inertial Reference Unit that allows you to navigate based solely on the output of the three datarefs, our simulated accelerometer.
However, there is one small problem you have to catch: X-Plane changes it's openGL reference frame as you travel (everytime you see new scenery being loaded, there also occurs a shift in the coordinate reference). So I need a little trick: Watch the datarefs for the reference point, and if they change, do an XPLMWorldToLocal() transform of my estimated position and overwrite my integrated position with these new local coordinates. Note that this does NOT reset the position to the "true" position, it just changes the reference frame in which my second integration is running.
So now on to even more advanced FMS simulations for X-Plane!
C++ bei Chaosradio Express
Note to my english readers: This is a link to a german podcast.
Endlich bin ich dazu gekommen, mir die vielversprechende Folge über C++ anzuhören. Chaosradio Express steht seit etwa einem Jahr bei mir ganz oben auf der Liste der abzugrasenden Informationsquellen. Ich habe aufgehört zu zählen, wie viele Stunden ich schon damit verbracht habe, mir Folgen dieses hochinformativen Podcasts reinzuziehen. Jede einzelne war eine Erleuchtung.
So auch diese (schon recht alte) Folge über das "Programmieruniversum" C++. C++ ist mehr als eine Sprache, es ist eine Art zu Denken. Das wird hier sehr gut herausgearbeitet, vorallem der Kontrast zwischen old-school Programmierer (Pavel, Gast) und Scriptsprachen-Jungspund (Tim, Moderator) sorgt für eine gute Spannung. Abzug in der B-Note gibt es für Pavel's Diktus - er spricht sehr langsam mit vielen "Füllwörtern". Im Zweifel einen Mediaplayer verwenden, wo man mit 1,3facher Geschwindigkeit abspielen kann. Dann ist es perfekt.
http://chaosradio.ccc.de/cre063.html
Wer einen Einblick in das C++ Universum braucht, kommt voll auf seine Kosten. Anhören! Sofort!
Lean Assist 1.4 update is out
I updated LeanAssist to include even more functions of the original JPI EDM-800 instrument. The latest update includes all modes brought to you by installing GPS tie-in and fuel flow sensor tie-in. So this is the top-notch installation you can have on your aircraft.
To learn more about the real thing, I strongly suggest reading the original manual, which is available online from JPI : http://www.jpitech.com/pilotguide.php
As always, the download of the latest version is on the x-plane.org download manager: http://forums.x-plane.org/index.php?app=downloads&showfile=12206
















