Wednesday, April 24, 2024

Flight Coloring as an error checking tool

In the pharmaceutical world, it's called "off label use;" in social media, it's called "life hacks."  Regardless of the name, I love it when folks find a way to repurpose functionality that was designed for one problem to solve something for which it wasn't designed.

MyFlightbook has "flight coloring" functionality, whereby you can give a name to a search and highlight flights that match the conditions of the search with one color or another.  The idea was that you might highlight your checkrides, or you might highlight charity flights.

I confess I had not really thought about flights that might match several such searches, and therefore I hadn't thought about which color "wins" in such a scenario.  Why would I?

But the other day, a user asked me about this because he was using flight coloring to highlight his airline flights (so jet flights where he was SIC and was flying under Part 121) and wanted to find the flights where he had neglected to tag the flight as having been conducted under Part 121.  He had set up a saved search with one color to highlight all of the Jet/SIC flights, and another search with a different color for Jet/SIC flights that lacked the "Part 121 Flight" property.  

The (brilliant) idea here is that the different colors could help him visually identify inconsistencies in his logbook that he wanted to catch: if he logged a flight and forgot to tag it as part 121, it would show up differently, immediately showing him that he had forgotten something.

I love this idea and I think it can be applied to all sorts of consistency checks that pilots may want to do.  But alas, it only works if you know the answer to the question above: what happens if a flight matches multiple searches?  When I implemented flight coloring, I never really gave much thought to that; I just left it undefined, and when he asked, I thought it might even be non-deterministic which color wins.

Well, I took a look at the code to answer the pilot's question about this, and it is deterministic; since this is a great "off-label" use for the functionality, I think I will formally commit to preserving this functionality.

Specifically, the rule is pretty simple: to determine the color (if any) for a flight, I test a flight against any colored searches in alphabetical order by the name of the search, and apply the color of the first search that matches.

So in the scenario above, the pilot would define two searches:

  • A query that checks for a flight that is in a jet and which has SIC time logged, and which does NOT have a part 121 property, with color A, and a title like "1. Jet/SIC - No 121"
  • A second query that checks for a flight in a jet with SIC time logged, with color B, and a title like "2. Jet/SIC".
The title's are entirely arbitrary, but notice that the "1." prefix and the "2." prefix ensures that the no-121 search sorts ahead of other jet flights, and therefore is tested first.  

As a result, any flight that should have the Part 121 property will get color A while the other jet flights will get color B, ensuring that the errant entries get the correct color.  

Note that if the two searches were named in the reverse order then all jet flights with SIC would get color B because that (broader) search would be tested first, and that's not the behavior you want.

The key point here is that you want to go from subset to superset.  I.e., the first search (alphabetically) should yield results that are narrower than a subsequent broader search; otherwise, the broader search will eclipse the narrower one.

Tuesday, January 30, 2024

Support for local time on import

I've received many requests over the years to support local time when importing flights, particularly for airline pilots who have schedules that are provided in local time.

I've always pushed back on this because it's a genuinely hard problem to solve; see my earlier post about what was required.

Alas, perhaps I just needed to look harder.  I finally found the right google queries to lead me to a free library that will convert a latitude/longitude to a time zone descriptor that is sufficiently narrow that a second library can convert local time to UTC with reasonable reliability.

Finding the right tools was absolutely the hard part; integrating them was somewhat easier, so now on import there is an option that allows you to tell MyFlightbook that your input times (block out/in, engine start/stop, or flight start/stop) are in local time rather than in UTC.

I still advice using this option with care.  Allow me to explain why.

I can't do any meaningful computation on local times; everything has to be UTC for autofill to work.  Think about it: Boston to Chicago is a 2-hour flight by jet, but Chicago is an hour behind Boston.  So if you leave Boston at 10am, you arrive at 11am; if you leave Chicago at 10am, you arrive in Boston at 1pm.  Using local times would imply that it's three times as long to go eastbound than westbound!

But how and when do I convert from local to UTC?

  • First, if you have a flight path (GPX, KML, CSV recorded by the app), then I already have time-stamped UTC data and I don't do any local time conversion.  There's no need - the flight path tells me everything I need, so the block/engine/flight times you provide can be safely ignored
  • If you don't have a flight path, I look at the airports in your route.  If I don't find EXACTLY two airports that match distinct airports in the database, then I can't determine which airport was when, so I cannot do any local time conversion.  In this case, minimal autofill will happen, but you could still find some wonkiness: if you didn't specify a total time, then I will compute it as if everything were UTC.  So in the Chicago example above, you might get 1 hour or 3 hours instead of 2.  (If you've provided a total time, though, then it won't be overwritten).
  • If you do, however, have exactly 2 airports in your route, then the system will:
    • Compute the UTC time at departure and destination, using the airport's latitudes/longitudes and the block/engine/flight times as appropriate, converting them from their respective local times.
    • Construct a synthetic great-circle path from A to B and evenly space out the samples across it, both in space and in time.
    • Clear the provided Total Time and Night Time fields (and a few others) so that they can be recomputed from the path.
    • "Fly" the synthetic path to determine things like night and total time, filling them in and computing engine start/stop, in UTC, based on the start/end of the path 
All of the last step above is the same as what happens when you provide UTC dates, except for the conversion from local time, but that's where the risk lies.  It's possible for these conversions to fail for a variety of reasons:
  • The lookup of the time zone from the latitude/longitude may not hit a time zone
  • The time zone returned by the lookup may not be found by the system that converts from local time
  • Time zone boundaries and rules (especially daylight time!) change over time and may not be reflected correctly
  • There are (rare) corner cases that are fundamentally problematic.  E.g., 1:30am on the date that DST ends is ambiguous because it occurs twice, and it is problematic on the date that DST begins because it doesn't happen at all!!
I should note that I decided against (at least for now) supporting local-to-UTC conversion when you press Autofill on the website or on the mobile apps.  The mobile apps were actually a really easy decision: you're not importing local times on the mobile apps.  They're either collecting data in real time (and they know their offset) or are working from GPX/KML files, which are all UTC.

But the website gets more complicated because you can specify a "preferred time zone", and that makes things very confusing.  Note that a "preferred time zone" is NOT a local time zone.  I live in Seattle, and I use Pacific Time as my preferred time zone.  All the preferred time zone means is that UTC dates are converted to and from PST for display.  So when I go out and shoot approaches from 10am to 1pm (=1800Z-2100Z, at least this time of year), it's stored under the covers as 1800Z-2100Z, but it's displayed as 10am-1pm.  

But there are at least two places where preferred time zones and local time zones collide.  One is if you have a flight that crosses time zones.  So if I fly from Boston to Chicago from 10am EST to 11am CST, that's stored under the covers as 1500Z to 1700Z, and it will be (correctly!) displayed for me in my preferred time zone of PST as 7am to 9am.  Notice that the preferred time zone is consistent across both departure and arrival!  So this gets to another problem which is that there's no easy way to indicate a non-preferred time zone for one field vs. another when doing data entry - they're all in one time zone.  So there's really no good way in the UI to indicate "local" time vs. preferred-time-zone time, much less departure-time-zone vs. arrival-time-zone.

The other is that the conversion to/from preferred time-zone is done "right now".  As I type this, it is January of 2024, so Pacific Time is 8 hours behind UTC.  Super - if I put in 10am, then under the covers it will become 1800Z.  But if I do this for a flight back in July, then it will do the same thing, but the time then was of course 1700Z because of DST.

That's probably all way more detail than you wanted to know, but I wanted to share some of the complexities here.

Enjoy the new feature - with care! - but it's still best to do everything in UTC.

Monday, July 31, 2023

Deadline Improvements for Aircraft

 Right now, you can create a custom deadline and tie it to an aircraft using hours rather than a date.  E.g., “Inspect Brake Pads at 200 hours.”  That would show up in your currency as a black unitless number:

It's black because while I can use the passage of time to gauge how close (or beyond!) you are getting to a date-based deadline, I have no such way to estimate how close the aircraft is getting to an hours-based deadline.  Further, since the hours are unitless, I don't know whether they are hobbs or tach based.

Today, I've I made two changes.

The first is to make hour-based deadlines more sensitive to an estimate of where you are relative to the deadline.  

Deadlines that are tied to an aircraft and which are hours based rather than date based will now find a high-water-mark ending hobbs and/or tach for your flights in that aircraft.  They will figure out which one that exists and is closest to the target.  The idea being that if your tach is, say, at 1,385 but your hobbs is at 4,801, and you set a deadline for 4,800, you probably meant hobbs and not tach.  

The deadline then uses that value (if found) to do green/blue/red like other currency/deadline items.  I’m using red for overdue, blue for getting close (within 10 hours) and green for anything that is more than 10 hours away.  And I show my estimate of where the aircraft currently is:

Of course this is only an estimate: if you share the aircraft and someone else has flown it, or if you neglected to log an updated ending hobbs on your last flight, then the true value is certainly higher.  But it should be a pretty decent estimate, especially if it’s your plane and you are good about reliably logging this.  (Note: if you use the club functionality, you can scan across club user’s flights to find a likely better high water mark).

The second change is to apply this to oil changes, the hours-based deadline for aircraft that is so common that it's built-in to MyFlightbook. 

When you enter the hours that the last oil change was performed, I had been showing that value plus various time intervals to suggest when you might need to next change it.

But I decided that with the enhancement above to deadlines, the simplest way to do this is a custom deadline, and with the enhancement above, it’s easy to add that:

If you click on “Add a deadline using an interval of:”, then it creates the new deadline, associated with the aircraft (and with the default regeneration interval that you selected (in the example image above, it was set for 275 since the last oil change was at 250):

Due to the deadlines enhancement, it will show green/blue/red based on the estimate of airframe hours:

The nice thing about doing it this way is that you can track any AD or similar periodic task in the same manner. 

Tuesday, July 11, 2023

Visited Airport Colored Map

Today's fun new feature is a colored visited-airport map.  I'm calling it "Beta" for a few reasons I'll discuss below, but it works pretty well.

The idea is to color in countries* and (a limited number of) states/provinces in which you have recorded flights.

Here's mine:

This, of course, looks very similar to the Visited Airport map I've had for a long time that is based on Google Maps:

The Google Maps is nice because it's highly interactive, but I haven't found a way in Google Maps to do the one feature I thought would be cool, which is coloring in the countries and states you've visited.  So I found a free library that works pretty well and I decided to have some fun trying to integrate it; the result is above.

To try it out, go to Airports->Visited Airports->Countries/Regions and click "View a colored map of visited countries/regions/sub-regions (BETA)" to view your map.  This link is sensitive to any query you've done on the Visited Airports page, so if you change your search criteria there, it will change in the map as well.

The colored map has some fun features:
  • You can turn the airport icons on/off (check/uncheck the "Facility" checkbox)
  • You can drag the map around and zoom it
  • You can see new places you visited by year (look in the right-hand bar) and even animate your visits over time using the play/pause bar on the bottom of the screen.
  • You can hover over a top-level region to see its details, and click on it to view the airports in that top-level region.
  • Click on an airport icon to view the name of the airport and the year you first visited it.
  • In the upper left corner, you can see the number of top-level regions, subregions (states/provinces/etc.), and airports.  Click on these to see details.
But it also has a few limitations.  

One is that the map it's based on is only coded with some states/provinces/subregions, so for now at least it will color in countries and then will add a darker color for states/provinces in the US, Canada, Australia, South Africa, Brazil, and Germany.  That's probably OK, since outside of these countries, most of MyFlightbook's sub-regions are small - more akin to counties, which only have a few airports each, so the coloring is less interesting.  

Another limitation is that while you can zoom and drag the image around, you can't "rotate" the global projection to put your particular region in a central top-down view. 

The code I am integrating also assumes a full document window, and I haven't done the engineering to put it into a "MyFlightbook" skin, so I'm opening this into a new window/tab so that you don't lose where you were in MyFlightbook.

Let me know what you think!

*As I disclaim on the Visited Airports page, I'm distinguishing "countries" from "nations."  This is meant to be a fun feature, and I am not making any statement about political divisions, only major geographic regions. E.g., Greenland is a territory of Denmark, but is treated as a top-level region in this taxonomy.

Monday, July 10, 2023

What time is it?

 Time is an interesting thing.  I won't even get into all of the weirdness that happens when you travel close to the speed of light that mean that time itself is inherently bound up with the question of who the observer is of that time.  Even in aviation, time is a messy concept.

In the world of MyFlightbook, there are at least 5 possible interpretations of a given time.

Specifically, if I say something happened at 3:00pm, that could mean any of the following:

  1. 1500Z, I.e., UTC time.  
  2. 3pm, but I've specified that my preferred time zone is Pacific Time, so in July that means 2200Z but in February that means 2300Z.
  3. 3pm "local" time (where you are).  E.g., I am in Seattle as I write this, and it is July so Seattle is currently 7 hours behind UTC, so this corresponds to 2200Z, but 3pm "local" in New York in July is only 4 hours behind UTC so corresponds to 1900Z.  It's actually worse than that, though: given shifting daylight time boundaries, "3pm local" even in a known location like Seattle might be 2200Z or 2300Z, depending on the time of year and which year it is!  
  4. 3pm PDT (or "1500Z-0700").  This is more specific than "local" time because PDT is defined as 7 hours behind UTC, regardless of time of year.  (In the winter, Seattle is PST - which is 8 hours behind UTC - but alas PST is not the same as PDT)
  5. 3pm.  Is that local?  PDT?  UTC?  You simply can't tell from the information provided.

You can see why aviation uses Zulu time.  If you don't use UTC, then a 2-hour duration jet flight between Boston and Chicago is 1 hour when going west and 3 hours when going east!!  And indeed, I can make a stronger statement: you can't do math if the times aren't in UTC.  As this Boston/Chicago example shows, the math only works if you are in a single time zone, and other calculations (namely night flight) go one step further and only work if the time is in UTC.  Anything else breaks things and renders any mathematical computation useless.

So for the vast majority of cases most things, MyFlightbook uses UTC (option 1 above).  If you tap "Tap for Now" on a time field in the MyFlightbook app, or put in a block in/out time or engine start/end time or similar, you are specifying a UTC time.  Under the covers, these are ALL stored in UTC.

Scenario #2 above involves a "preferred" time zone. On the website, there is an option (Profile->Preferences->Flight Entry and Display) where you can choose a preferred time zone (the 2nd option above, see image below).  If you choose this option, then all times are converted FROM UTC to the specified time zone (accounting for daylight saving) for display, and when you enter a time, they are converted from that time zone back TO UTC.

Note that this doesn't change based on your location.  If you set this and then travel to London, your times will still display in Pacific time even though you're not currently in Pacific time.  

It is super important to note that even if you have a preferred time zone, the underlying times are still all stored and processed in UTC!  I.e., it is simply a conversion applied for display and data entry purposes.

On the mobile apps (iOS/Android), you have an option to use local time (scenario #3 above).  "Local" in this scenario is the third bullet point above, and functions very much like the preferred time zone that I just described, except that the "preferred" time zone is reset as your phone/tablet travels from timezone to timezone. 

E.g., in the Boston/Chicago example above, suppose that it is July and I depart Boston at 10am local time (EDT) with the "use local time" option selected.  If I tap "Tap for now" on block-out, the system sets the block-out time to the current Zulu time of 1400Z (=10:00am local + 4 hours to UTC), but it displays that time converted to EDT as (1400 - 0400 =) 10:00am EDT - i.e., the local "Now".  

Now I fly 2 hours to Chicago and land.  It is now 1600Z - two hours later - but Chicago in the summer is 5 hours behind UTC, so when I tap "Tap for now" on block-in, the system sets the time to the current value of 1600Z, but it displays it as (1600 - 0500 =) 11:00am CDT, i.e., the local Now.  And if I look at the block-out time - which had said 10am when I left Boston - it now says 9:00am because we are now in Central Daylight time.  Of course, this is still the same 1400Z (9am = 1400 minus 5 hours) block-out time, just expressed in the new time zone.

As with preferred times zones, once again ALL times remain UTC under the covers; it is only the display (for both reading and entry) that changes!

So the subtle difference between these two examples is that "preferred time zone" is just that: an expression of my preference, which only changes if I explicitly change it.  Whereas "local" time can change simply by moving between time zones, with no explicit action on my part.

Most times on MyFlightbook can be handled in that manner, but there are a few cases where scenarios #4 and 5 rear their heads.

The mobile apps actually are guilty (?) of using scenario #4 when recording flight track data.  They capture the local time - so that you can view things in a time zone that makes sense for where you were when you flew the flight (which may not match either your preferred time zone of scenario #2 above, nor be your current time zone as in scenario #3 above).  They do this by capturing the local time in one column of the CSV track data, and including a second column ("TZOFFSET") which contains the number of minutes to add to the captured time in order to reliably compute a UTC time.  This works fine - "1500Z" with a 420 minute (7 hour) offset in Seattle can quite accurately compute a 2200Z UTC time, but still capture both the fact that the GPS sample in question was at 2200Z and at 3pm local.  And for this reason, when graphing, MyFlightbook does compute a UTC column that you can graph, but the default time stamps are the local time where you were when the sample was captured for ease of viewing.  Note, of course, that if you cross a time zone boundary, this means that the "date" data could repeat (since you are reliving an hour of local time), but the utc-date cannot repeat.

And...then there is scenario #5 above: a naked date with no clue about its time zone.  Is it a UTC time?  EDT?  PDT?  You simply can't tell; it is inherently ambiguous.  The biggest source of this problem is in user-supplied data, and the most common example of this is when users import flights using naked local times, and that's mostly from airline pilots uploading their schedules.  It's generally not a problem unless they check the box for MyFlightbook to compute things like night flight on their flights, and then everything falls apart.

Sadly, converting from local time to UTC is non-deterministic.  By definition, there are local times that either don't exist in UTC (2:30am on the day you "spring forward") or that exist twice in local time (2:30am on the day you "fall back").  Harder still is that the definition of who observes daylight time and on what schedule is not exactly static.  There are services that can take a latitude/longitude/date/time and return the UTC time, but they cost money (I'm a free service) and there is inherently latency involved with sending off a request and getting an  answer, so I have made the explicit decision not to use them.  As a result, naked dates are just...well, they're just pretty useless.

Finally, there is one place where the date-of-flight is actually impacted by your local time!  Europeans may not encounter this much, but Americans often will.  Although I'm not aware of any official guidance here from either the FAA, EASA or other agencies, the date you use for the date of flight seems to always be in local time.  But, for example, EASA specifies explicitly that the time of departure and time of arrival should be in UTC.

So in Europe, if I depart Amsterdam at 6pm and arrive Frankfurt at 7am on July 10 (when they are UTC+2), I could put July 10 as the date of flight, and use 2000 and 2100 as the departure/arrival times.

But now take that same example and move it to be LA to San Francisco.  I depart on July 10 at 6pm - which is July 11 0100Z! - and arrive on July 10 at 7pm, which is July 11 0300Z!

For this reason, there is a rather esoteric option in Profile->Preferences where you can say whether the "date of flight" field represents a UTC date or a local date:
This is primarily used to determine whether a simple time is shown for departure/arrival ("21:00" arrival in Frankfurt) or whether the time needs to be further qualified by a date ("July 11 2023 03:00") because it is distinct from the date-of-flight.

Monday, May 29, 2023

Anatomy of panic over site instability

This morning I had a bit of a sitemageddon, where the MyFlightbook website and service were only barely running for almost two hours.  Woke up, was about to sit down for breakfast at about 7:30 when "ping", I have a text from Google telling me that the site is down.

First step: look at the site monitoring.  Hmm, CPU is fine; that's usually the culprit.  And I can ping the server, so the server is up and running.  But when I try to browse to the site, I get a 503 error ("service unavailable") - yikes.  So I remote desktop in.

Let me back up just a bit: my stack is C# code targeting ASP.NET (with MySQL) on top of IIS (Internet Information Services - the web server) on top of Windows.  And when I remote desktop in to the server, I can see that Windows is up and running fine, but IIS has stopped.  I reset IIS, the site comes back and runs great, but within about 2 minutes, it's dead again.  Lather, rinse, repeat.  Something is wrong here.  

So I reboot the server.  Same behavior. Oh, shit, this is bad.  And - I assume - how can this be me?  IIS is stopping.  If it's my fault, that's like a bug in Excel causing a blue-screen of death in Windows.  Very bad.  And the CPU is spiking (80-100%, when it should be around 10-30%)  One process taking a lot of CPU is Windows Error Reporting, so I do some Googling to find out how to read its error reports and it says...just that the IIS process was being killed.

Yikes.  Meanwhile, I'm still resetting IIS every couple of minutes, and lots of you were getting 503 errors.

Look through EventViewer - I'm seeing two errors, both of which should be totally benign.  One is a bug in the MySQL code that I've been seeing for weeks; it's minor - it fails the first time, succeeds the next time.  The other is an exception in FlightStats.  (I'll come back to that one).  I haven't changed FlightStats code in years, so I ignore that and downgrade the version of MySQL to one that didn't generate the bug, and deploy that change.  That particular bug goes away but...I'm still resetting IIS every 2-3 minutes.

At this point I'm hungry and I haven't had any coffee, and I'm stressed - what on earth is going on?  So I take a deeper look at the FlightStats error.  It's an exception that's being generated because a dictionary lookup that shouldn't fail is failing.  This is code I've had (essentially unchanged) for years - why is it crashing now, and why doesn't it crash on my development machine?

So here is where it finally starts to come together.  Some additional Googling (while I'm still resetting IIS every 2-3 minutes!) reveals that IIS has a (news to me) "Rapid Fail Protection" feature to protect the system wherein if it has 5 exceptions in 5 minutes, it kills the process.  A bit more searching and I find that I can configure this, so I set it to 40 exceptions within 5 minutes and...lo and behold the site is staying up!  

Phew, after 2 hours of manually recycling IIS every 2-3 minutes, I can get some food and coffee into my belly.

But...what happened?  Exceptions are actually a pretty normal thing.  If I "catch" (handle) the exception, usually that's when you see an error message and it's benign, normal, and expected.  Sometimes, an exception arises that I don't catch.  When that happens, I have code that captures the exception information and emails it to me so that I can figure out the issue.  Not uncommon to have a few of these a day (generally all also from perfectly benign scenarios).  But alas, FlightStats is being computed in a background thread, and when the exception happens there, it wasn't being handled, and thus I was bumping into more than 5 exceptions in 5 minutes, and IIS was self-destructing.

That's a lot of background, but it matters here.  What is FlightStats and what was the issue?  On the home page and in a few other places, I show fun stats like "in the last 7 days, MyFlightbook users recorded X flights in Y aircraft to airports in Z countries" and so forth.  But these are slow to compute, so what I do is when they are first requested, I quickly return empty stats but I kick off a background process (that can take a few minutes) to gather the relevant stats, which I then cache for 30 minutes.  It's been this way - flawlessly - for literally years.  But today, that computation on the background thread was crashing, and thus the next time the home page was hit, it would kick off the process again, which would crash, and then...5 crashes in 5 minutes = kill the IIS thread.  And since it's triggered on the home page, that's enough to bump up the crash rate above the threshold.  40 crashes is enough to give me some breathing room, but it's still restarting the process with every crash, which is going to screw some user experiences.

But this worked fine for years, and worked fine on my development machine.  So I point my development machine to the production database (something I almost never do - that's insanely dangerous!) and I can isolate the bug.  

The issue is that part of the stats I retrieve are airport stats, including longest flights and similar, and so I need to dedupe airports.  E.g., "OGG" and "PHOG" are both Maui (IATA and ICAO, respectively), and because the two airports in the databases may come from different sources, the latitude/longitudes might differ slightly (usually only a few meters apart).  To dedupe them, I put them into a dictionary (a lookup structure) where the key is a hash of the latitude/longitude.  That way, two airports that have almost identical locations will have the same key, and thus coalesce.  The key I'm using is a simple concatenation of the latitude and longitude - both rounded to 2 digits.  

And finally, this is where the bug is.  There's a user-defined airport that somebody logged in the last 30 days that has a latitude/longitude of 53.9522222222, -1.1749999999999545.  So the dictionary key for this is (remember, rounded to 2 places) "ALa53.95Lo-1.17".  ("A" for airport, "La" for latitude, "Lo" for longitude).  I put the airport into the dictionary with that key, and (in theory), I should be able to read it back with the same key.

But alas, the code was crashing because when I tried to read it back with the same key, it wasn't in the dictionary and threw an exception.  Notice all of those 9's" in the longitude?  Rounding that to 2 digits properly rounds to -1.17, but there were some circumstances (I still needed to debug) where it would round in the key as -1.18.  Alas, if you put something into the dictionary with a key of "ALa53.95Lo-1.17" and try to read it back with "ALa53.95Lo-1.18", it fails.

The very quick fix was to adjust the airport to use a longitude of -1.17498.  Wahoo!  No more IIS recycling; I can reset the Rapid Fail Protection to 5 errors in 5 minutes.

The next band-aid was to deploy a fix to production that checks for whether an item is in the dictionary before attempting to read it and doing something innocuous if it's not there.  So more rounding errors can't cause this again.  At this point, it's been almost 4 hours since the site started acting up, so that was good for me for the day and I went for my walk.

My next task - which I can do at a more leisurely pace (thankfully!) - is to figure out why -1.1749999999999545 is somehow becoming -1.18.  My best hypothesis - totally unconfirmed as yet - is that -1.1749999999999545 is being rounded somewhere to -1.175, and *that* (correctly) rounds to -1.18.  I am tracking that bug and will look into it later this week.

So in the end, it's a data issue!  (Well, my failure to handle it was obviously the bigger issue, but it was something that could be fixed with a simple data change!!)


I was correct that somewhere something was getting rounded from -1.174999… to -1.175

Here’s what’s happening: The FlightStats process is using each airport to create an “AirportStats” object.  When it does this, the AirportStats does a deep copy of the airport, using a utility that just blindly enumerates the airport’s read/write properties and writes them to the AirportStats object.  So far so good

But for old legacy reasons, the airport object has a Latitude (String) and Longitude (String) property (in addition to a decimal LatLong property), and these use 8 digits of precision; getting one of these values returns the underlying decimal value as a string formatted to 8 decimal places, and if you set it, it sets the underlying decimal value to the converted string.  I don’t use these properties anywhere, but the deep copy sees them and dutifully reads from the source and writes to the destination.  While the order of copying is undefined, it looks like it happens to be copying the string values after copying the underlying decimal values, and thus it overwrites.  And that is where -1.1749999999999545 becomes -1.175.

I am storing into the dictionary using the key generated on the source airport (-1.174999...), but later when I retrieve the value I am doing so based on the AirportStats object's key.  But alas, the AirportStats object – due to this copy functionality – now has “-1.175”.  And that – quite correctly – rounds to -1.18.  Later in the code, when I’m reading from the dictionary, I’m using the AirportStats’ key, which is now different.

Trivial fix: since Latitude (String) and Longitude (String) are legacy/deprecated, I’m simply commenting out the code to set them.   You can still read display strings of the underlying latitude/longitude (extracted from the latlong object), but writing it is a no-op.  I could *probably* remove these properties altogether, but there’s a slight possibility of breaking a mobile app, so I’m going to leave them there.

Monday, March 13, 2023

Import and ambiguous aircraft

A pilot today had a question about how to do an import when a tail number has been re-used across multiple airframes/models.

There are really two scenarios here.  The simple one is where there are multiple versions of an aircraft, but you've only flown one model.  The more complex scenario is where you've flown Nxxxx as, say, a C-172 and as a Kodiak.

If the former, this is pretty simple:

  • Add Nxxxx to your account.  
  • You might get the wrong version if the wrong version is also in the system; that’s fine – if so, on the website go to Aircraft->My Aircraft, click on Nxxxx and you’ll see a section titled “Other versions of this aircraft.” Click on “My flights are in this aircraft” next to the version you want.  If you don’t see your version at all, then click the pencil icon next to the model and you can edit the model.  The system will see the change as a “major” change (assuming that you’re changing from, say, a 172 to a 182 but not from a 172 N to a 172 S) and will clone the aircraft automatically and put you into the clone.
  • Now, import your file.  Since you only have one Nxxxx in your account, it will pick up the correct version.

If the latter (you've flown the same tail across different models), the process requires a few more steps:

  • Add Nxxxx to your account.
  • If the alternate version of Nxxxx is already there, then click on “Don't change any flights, but add this aircraft” next to the other version.  This will allow multiple versions of Nxxx to coexist in your account side-by-side
  • On the other hand, if you don’t see the other version, then click the pencil next to the model name and you can edit it to be the other version; this will create a clone of the aircraft and switch you to that clone; you should then be able to click “Don't change any flights, but add this aircraft” in the clone.  I.e., if Nxxxx is the 172 and you don’t see the Kodiak, then edit it to be the Kodiak and now it will be the Kodiak but not the 172.  You can then click to add the 172 alongside the Kodiak
  • One caveat to the above step: if you’re the only pilot in an aircraft, it won’t clone.  Look at the top where it tells you how many pilots use the aircraft:

    If it says it’s used by multiple users, then the step above will work.  If it says only 1 user, that’s you and the edit to the model will actually modify the underlying model instead of cloning.  If so, let me know the aicraft and the model you want and I can clone it for you.
  • Once you have all of the relevant versions of Nxxxx in the system, you need to get the unique ID for of each variant.  Go to Aircraft->My  Aircraft and click on one of the versions of Nxxx and look in the address bar:

    Right after “…id=” you’ll see a number.  That’s the unique identifier for that specific aircraft.
  • Now, go to the CSV that you’re using to import.  Add a column titled “Aircraft ID”; I suggest putting this next to the Tail Number column, but it doesn’t really matter.  For the aircraft that might be ambiguous, fill in the correct ID in the “Aircraft ID” column.  You don’t need to do this for the unambiguous aircraft because the tail number is sufficient.

    E.g., suppose I had two versions of N30322 but only one version of all my other aircraft, then my spreadsheet might look something like this (aircraft ID's below are made up for illustration)
You should now be able to import.  For the sample screenshot above, the system will see, for example, N2939J and there'd be no ambiguity because there's only one version of N2939J in your account.  But for N30322, it would use the Aircraft ID column to determine whether it needs to use 38473 or 103948.

Sunday, February 5, 2023

61.65 and Sims/Training Devices

Ever since I implemented support for instrument ratings progress for Instrument ratings (61.65(d)), there were two pesky problems regarding the use of simulators/training devices toward 61.65(d)(2)'s forty-hour requirement.  Namely, 

  • 61.65(h) allows 30 hours (rather than 20) of FTD/FFS (Flight Training Device/Full Flight Simulator) time if your training is conducted under part 142, and 
  • 61.65(i) distinguishes BATD (Basic Aviation Training Device) time from AATD (Advanced Aviation Training Device) time, allowing 10 hours for BATD and 20 hours for AATD.

I don't have a reliable way to tell if a given training session was performed under part 142, and I don't distinguish AATDs and BATDs anywhere else in the code (no real reason to do so), so I did the conservative thing and allowed up to 20 hours of FTD time or 10 hours of ATD time (limited to 20 hours total, per 61.65(j)).

Simple, and safe (won't overstate your time), but possibly cheats you out of some experience. So today I've taken out a change that addresses both of these changes.  

The part 142 one is easy: there are three new instrument rating progress items for 61.65 (d)-(f) where you simply declare that you want to see your progress assuming part 142.  If you choose that rather than the plain 61.65 ratings progress, then I will apply the 30 hour FTD/FFS limit rather than the 20 hour limit.  (Note that the 30 hour limit does not apply to ATD time!)

The ATD one is a bit more of a hack.  If the flight "aircraft" is identified in MyFlightbook as being an ATD (not enough to simply name it that - you have to add it and specify that it's an ATD), then I look at the model's name and the flight's comments.  If either of these contain the word "AATD", then I assume it is an AATD; if not, it is a BATD.

There's an interesting problem here, though - and I'd love if folks could weigh in.  61.65(i) allows up to 10 hours of BATD time OR 20 hours of AATD time.  The word "OR" there is interesting - it means there is a choice.  Other places where the FAA uses the word, they mean it as an exclusive "on or the other, but not both" option (e.g., see the "Grannis" interpretation referenced here).  FAA also usually uses words like "and" or "combination" when they mean you can mix and match.  

I have not seen an official interpretation on this, but I'm still going to be conservative and assume that "or" means "or".  So if you have, say, 9 hours of AATD time and 9 hours of BATD time, you can only count 9 hours of ATD time towards the 20 hour limit imposed by 61.65(j).  I don't know if that was the FAA's intent, but it's how I read the words.

Anyhow, here's some details on the math

  • I compute your Aircraft time as the sum of your instrument time in real aircraft.
  • Per 61.65(h), I compute FTD/FFS time as
       MIN(20, FTD/FFS instrument time)
  • I also compute FTD/FFS-142 time as
       0 or MIN(30, FTD/FFS instrument time)
    depending on whether you are using the part-142 Training variant.
  • Per 61.65(i) I compute ATD time as
       MAX(MIN(10, BATD instrument time), MIN(20, AATD instrument time))
    So for now, at least, if you have 9 hours of BATD time and 9 hours of AATD time, that’s just 9 hours, due to the “OR” in the reg.  Easy to change if I get an authority to say they can be combined.
  • Per 61.65(j), I then compute your Non-142 Sim time as
       MIN(20, FTD/FFS time + ATD time)
  • Finally, I compute your total experience as:
        Aircraft time + MAX(FTD/FFS-142 time, Non-142 Sim time)
Let me know if you have any reference on mixing/matching BATD and AATD time here.

Thursday, November 17, 2022

Creating a good looking paper logbook

If you've read earlier posts on this blog (such as here and here)  then you are probably aware of my opinion regarding the very concept of committing an electronic logbook to ink-and-paper.  Add to that the fact that there are superior alternative methods (such as a live link to one's logbook, or even a simple PDF) for sharing your logbook with a regulator, insurer, or interviewer, and I palm my forehead that anybody bothers.

But alas, I'm not everybody. And I can't expect everybody to share my perspectives, especially when the aforementioned regulators, insurers, or interviewers often also do not share my perspectives.

For that reason, I've made sure that MyFlightbook's support for printing is as rich, customizable, and full-featured as I can, including (currently) 14 different layouts, and a host of customization options.  

So that gives you the tools to print out your logbook.  But it begs the more subjective question: how do you make it look really good and really professional?

One user on the site named Malcolm who has invested a lot of time on this has shared with me what he learned, and I thought I'd share it here so that everyone can benefit.  (Thanks, Malcolm!)

Obviously, everyone's flying experience is unique, so the choices he made below are specific to his logbook, but hopefully his example will point people in the correct direction.

Allow me to start with a view of his finished product so that you can see what is possible:

Task 1: Select a layout

Each of the various layouts on MyFlightbook is designed to optimize for certain information (e.g., the glider layout has columns for various types of glider launches, the naval layout has carrier landings), for regulatory compliance (the EASA layout does its best to conform to the FCL.050 specification) or "familiarity" (e.g., the US layout tries to look like common US-based paper logbooks).  

Given what you want to emphasize or project, pick the layout that works best for you.

You may also wish to add additional columns (most, but not all layouts can support that) and/or customize how per-page subtotals are shown.

My personal opinion on per-page subtotals: don't.  Just don't.  They serve no purpose in an electronic world; they only existed to facilitate manual summation of fields, and they end up wasting paper.  They're just plain stupid.  Use "Continuous".  If you must use per page subtotals, you may have pagination issues.  See the FAQ for how to manually force a page break if the flights would otherwise spill onto another page.

You can also select some additional options regarding verbosity of model descriptions or font-size; this is entirely up to your personal preference.

Task 2: Get rid of the cruft

There is a ton of data that you might log on a flight that you want to track, but which is not necessarily relevant for a job interview or ramp check.  Below is a screenshot from Malcolm's logbook of data that he felt was important to track but not important to print.

Note as well that anything that you put after "///" in comments will also automatically be excluded from printing.

Some layouts support images - if you're printing as a memory book, you will almost certainly want to include these.  If it's for a job interview, you almost certainly do not.  

Signatures (available in all layouts) is a different story - you may want them to demonstrate compliance, or they may be noise to you if your training was long ago.

Task 3: Save it for easy future access

At the top of the print-view screen is a link icon (, yes I admit I'm a terrible artist).  If you click that, your browser will change the URL in your address bar to a big long URL that will take you back to this print view with exactly these layout and content settings.  Bookmark that; it will save you time if you need to come back and print again later.

Task 4: Set up your print layout

Landscape (wide) format almost always works best, since horizontal space is most critical for fitting everything.

Set both side margins at least 12mm in order to make the blank margin wide enough that the hole punch would not obliterate some of the data. Unfortunately, the PDF generator I am using at the moment does not support mirrored margins (where the inside margin is wider than the outside margin in order to accommodate the punch holes)

Page size is obviously up to you and what is available to you.

Click "Create a PDF to download".  Review the results and tweak until you are satisfied.

You're now ready to print.

Task 5: Print

Set your printer to print landscape, double-sided ("flip on short side") and print!

Task 6: Bind

Binding your logbook definitely gives it a professional look and even if it doesn't say anything about your competency as a safe pilot, it does demonstrate your attention to detail.  

Malcolm found two options for the binder:

  1. Buy a 3-ring landscape format binder such as this one. The only one I was able to find was unnecessarily wide (1.5" capacity) but would work fine.

  2. Or, buy a checkbook binder. These come with 7 rings in it. Using a heavy pair of pliers, it is easy to twist out four of the 7 rings and pull them out.

Since you're printing in landscape, the location for the holes will be different than in portrait mode.  As a result, punching holes will require a 3-hole punch with adjustable hole positions.  These are usually available in an office, print shop, or office supply store.  If you don't have this, you can take your printout to Staples or some other copy shop and pay them to punch the holes in it. (Bring the binder so they can be sure to put the holes in the right place. Show them the format of the binder and warn them not to punch the long side of the paper, since this is what they would be accustomed to doing.)

Malcolm also recommends using post-it arrows pointing to each checkride; his interviewer remarked on how much he appreciated that.  

I'll add two more options to consider in this vein:

  • You can use flight coloring to accomplish the same goal, if you print in color, though the post-its can still help to quickly find the page containing the relevant flights.
  • Or, if you skip paper and share electronically, search functionality works pretty well to quickly find check rides (or other relevant experience), rendering this all moot
I have heard from several users that there are vendors who sell fancy binders for this purpose, often at inflated prices.  But it seems that this is not necessary - pilots have received job offers using binder clips (or electronic!), and I've never heard anybody be turned down due to the lack of a binder, so my personal suggestion is that the only valid reason to go to the effort or expense of a fancy binder is if it makes YOU happy.

Got any other tips for good looking printouts?  Let me know!

Wednesday, October 26, 2022

Tracking changes made to a flight

This is probably the most passive aggressive feature I've ever done.  I'll explain why, but first let me explain the functionality.

I've had a few requests for the ability to track changes made to a flight since it was first saved, due to local authorities in a few jurisdictions deciding that doing so was a requirement for electronic logbooks.  

I already have a form of this functionality already for signed flights. When a flight is signed, I save a copy of the salient (I'll discuss "salient" below) details of the flight (I refer to it as a hash, but it technically isn't one) so that I can always tell if any of those "salient details" have been modified since being signed.  This is important because the signature is an attestation to the facts of a flight, and the entry no longer represents what was certified by the signing authority.  Details are here, but this is important and useful functionality to verify that the signature is valid.  As a bonus, the hash is saved in such a manner that I can determine which edits have been performed, allowing the student to undo their changes (and thus re-validate the signature if it once again matches its original signed state).

The new feature expands this ability to unsigned flights.  If you enable this functionality, then when you save a new flight, it will also produce the hash.  If you then modify that entry, you'll see the modifications in-line (on the web) and you'll see the fact of the modification in a print view (an asterisk next to the date; I don't want to waste ink and muck up the layout for purposes of showing the changes).  As with signed flights, if you revert the changes, then its goes back to being "unmodified".  If a flight is signed, then the flight is "reset" and the version that is signed provides the new baseline for modifications.  Note that enabling this feature does NOT turn on tracking for existing unsigned flights.

But it's a really stupid feature that just clutters your workspace and doesn't even solve the problem that I believe it is trying to solve.  It literally adds no value.

Allow me to back up and get philosophical for a moment.

I've found in life that one of the most useful questions is "what problem are you solving."  

It's incredibly powerful for two reasons.  The first is because the better you understand a problem, the more self-evident workable solutions become and the more clearly you can weigh one against another.  

But the other is that it helps us recognize when we already have a solution in mind that is blinding us to other, potentially better solutions.  (Heck, sometimes it can even help us figure out that we don't really have a problem at all!)

Tracking changes to un-signed flights is a solution, not a problem, so if I ask the question "what problem does it solve", I realize that nobody has addressed this head-on.

I can only guess that it has to do with verifying that nobody is fudging things and backfilling data.  

But if that's the problem to solve, tracking changes is a terrible solution, for a variety of reasons:

  • It's ineffective, because it is easily worked around.  After all, if you delete a flight and add a replacement flight, there's no way to determine that the newly entered flight was a replacement for the deleted flight.  (And if you have to track deletions, then doing bulk-edits or multiple tries when you import your logbook makes you look like a terrible fraudster).  We pilots already operate on an honor system: if I have an entry from a solo flight I made on Friday, that is credible only because I say that I actually took that flight.  I could be lying to pad my logbook, and change tracking does nothing to help that.
  • It's arbitrary, because it imposes a requirement beyond what paper has, while paper is still acceptable.  In a paper logbook, you can easily insert time (say, night flying or solo time) in a manner that cannot be detected (even for signed flights).  I've heard the counter argument that many edits (deleting flights, modifying existing data) cannot easily be done because you'll see the scratch-out or the "3" clumsily turned into an "8"; while that's true, it's also pretty easy to just get a new paper logbook and transcribe entries if you're really determined.  But again, since I'd think the most common source of fraud here would be adding non-existent experience (what value is there in hiding actual experience?), that's an edge case: it's easy to insert additional landings, approaches, or times into existing flights - or even entire new entries - on paper in an undetectable manner.  Thus a different standard is being applied to logbooks based on the medium, but with no clear rationale for doing so.
  • It's ambiguous.  I mentioned "salient details" above. I do this because there are a lot of things that simply do not matter to the integrity of a record: if you add or remove images/videos, or if you make it public or private for example (neither of which even exists in the paper world).  For signed flights, I've excluded such values from the hash to avoid needlessly invalidating a signature.  I've yet to see any authority asking for change tracking actually define what constitutes a "change" to be tracked.  The requirement is similarly vague about what, precisely needs to be recorded about each change or whether it is OK to treat the flight as unmodified if you make a change and then change it back.
  • It's insanely noisy.  If you fix a misspelling, if you remember that you forgot to log cross-country time on a given flight, if you do anything to correct a legitimate error, it flags the flight as being modified.  Good luck distinguishing fraud from legitimate edits.  (My old paper logbook is full of such corrections/additions, as I fixed math errors or went back to add experience that I had acquired but hadn't logged at the time).
  • Worst of all, it's completely pointless because it is redundant.  Particularly if you submit a printed version at a job interview or to authorities, you are already affirming that "yes, this is my true and accurate logbook"; that's why there's a place to sign each page.  And, of course, signed flights already have the mechanism so there's an additional layer there for training where a 3rd party is certifying the truth of the records.
And if that's not the goal, then...seriously, what problem is being solved?  I want to know!  I have no other theories, but perhaps someone here does?  

Anyhow, this silly and boneheaded functionality is now live.

Saturday, September 10, 2022

Slicing and dicing your Data

One of the critical benefits of having your logbook online is that it gives you very rich ways to analyze your flying data.  Today I'm going to discuss 6 different ways (and refer to two others!) that you can slice and dice your flying to gain insights, answer questions, or demonstrate experience.  I'll discuss this in the context of the website, but most of this functionality is also directly available in the iOS or Android apps.


The first and simplest is, of course, totals.  By default, Totals shows you, well, umm, everything that can be summed up.  Internally, believe it or not, while there's a core set of known things to total, the list is not fully pre-determined; it's largely up to you and what's in your logbook.

Here's my totals as of today:
I'm showing the drop-menu in the upper right corner to demonstrate that you can choose whether to have a big long flat list, or to have (as I have done here) totals grouped together in appropriate ways.  (Grouping is applied on the website; totals on the iOS or Android app are always grouped) 

The first group, by default, shows you your total times broken down by category, class, and (if a type rating is required), type.  An example of "type rating" in the US would be a 737, which requires a "B-737" type rating to fly.  If I had some 737 time, then you'd see 737 time broken out as a subset of AMEL time. 

If you like, you can change this to group by model instead of by category/class (go to Profile->Preferences->Currency/Totals; this will be reflected on iOS or Android apps, but you can only make the change on the website).  You can define a "model" for this purpose as either being a very specific model ("C-172 S" is distinct from "C-172 P") or by ICAO code (both a C-172 S and a C-172 P are "C172").  This creates a new section with your totals by model.  In my case, it looks like this when I use the more specific model definition:

The next sections are times by aircraft feature (complex, high-performance, turbine, tailwheel, etc.), your "core" times (Night, Cross-country, IMC, etc.), other totals (Carrier landings, ILS approaches, etc. - depending on what you've logged), and finally your bottom line total flights and hours.  

"Hours" for purposes of total time and for the category/class and model times is what you've logged in the "Total Flight Time" field.  So if you had a 1 hour session in a sim emulating a Boeing 737, if you logged an hour of total time, that will count as an hour of total time (overall), an hour of AMEL time, and an hour of AMEL (B-737) time.  (This is why, in general, you should NOT log sim time in the "Total Flight Time" field.  See my previous posts on this topic).

I titled this post "Slicing and dicing" and yes, you can slice/dice your totals.  Totals are search sensitive!  Notice that most of the totals above are blue, indicating that you can click on them (you can tap on them in the iOS/Android apps).  This does a search, and then re-displays the totals but only for the flights that matched the search.  

So, for example, if I tap on the AMEL total in one of the links above (or do a search for only flights in MEL airplanes and then click totals) I get this:

Notice the bar above that says only AMEL flights are included (and, not surprisingly, as a result my ASEL and Glider totals are zero and hence not shown).  So if I want to know my PIC time in MEL airplanes, I can just read it here: 19.23 hours.

Totals are a powerful way to get results for common queries - especially from insurance companies who seem to come up with all sorts of obscure totals that they want to know.

8710/IACRA form

The next three ways to slice/dice your data are over on the Training tab (both website and iOS/Android apps).  The first is the 8710/IACRA form.  Apologies for folks outside of the US - though this is likely useful to you nevertheless! This report mimics the 8710/IACRA form that pilots must fill out before taking an FAA checkride.

Once again, I'll be the guinea pig and use my data for an example:

This should be pretty self-explanatory and straightforward.  The two key things to note are:
  • Read the footnotes!  I specifically disallow a lot of "combination" values like "night PIC" because I can compute them.
  • As with totals, this form is also search-sensitive.  So if you, for example, log a lot of Microsoft Flight Sim time (doesn't count towards ratings) or want to segregate, say, your military time from your civilian time, you can do the appropriate search to include only the relevant flights and the form will update.

Rollup By Model

Also on the Training tab, this emulates the common Airline Apps page report and hopefully provides all of the relevant information for applying to many airlines (at least here in the US).  Again, my data:

Note that this is also broken down by model (ICAO code) and is search sensitive.

Rollup By Time

The final report on the Training tab provides the same data that general Totals provides, but for multiple common time periods.  My sample (truncated):

This can be a quick way to view your totals for particular time periods.  It is, of course, also search sensitive.  


Going back to the Logbook tab - and into the first functionality that I will discuss that is web-only - is the Analysis tab.  This is where you can view your flying trends graphically.

Here's my default analysis view:

In the example above, note that I am hovering over one of the bars and seeing that (in this example), I flew 84.3 hours in 2017.  

The gist of how the analysis tab works is that you can select a value to sum for the Y axis ("Total flight time" in the example above), and a grouping construct ("Year" in the example above) for the X-axis.  MyFlightbook looks at all of the flights that match your specified search criteria (you thought maybe it wasn't search sensitive?  Silly you...), and puts then each into an appropriate "bucket" for the x-axis based on the grouping.  It then sums up the requested value for each of the flights in each bucket.  (If this sounds a lot like a pivot table in Excel, that's not accidental...we'll get there below...)

This can be pretty powerful, or merely fun.  For example, if I want to know how my flying varies by day of week, I can do that:

Or, I can group by model of aircraft and show the average so I can see which models I've flown more or less than average:
The x- and y-axes are determined by your flying, so you may be able to do even more interesting analysis. 

Finally, I'll point out that you can view and download a table of all of your flying filtered (i.e., search sensitivity) and grouped as you've selected, in case you want to do a deeper dive.

Pivot Tables

The final piece of data slicing and dicing you can do with MyFlightbook technically isn't even in MyFlightbook proper; it's in spreadsheet apps like Excel or Google Sheets.

You can download your entire logbook into a CSV file by going to Logbook->Download on the website, and from there loading it into your spreadsheet of choice. 

From there, you can apply formulas and create pivot tables to your heart's content, which will allow you to group to multiple levels (e.g., group by category/class, then by model, for example, whereas MyFlightbook's grouping only goes one level deep) or you can customize charts or do filtering searches that MyFlightbook can't do.

Using this functionality is beyond the scope of this blog post - especially since it is dependent on your spreadsheet choice.  But it is yet another level of analysis that is available to you.

Other Data Analysis

There are two other data slicing/dicing options that are worth at least mentioning.

One is airports.  You can view the airports that you've visited by going to Airports->Visited Airports (on the website; just "Visited" on the mobile apps).  Again, this is search sensitive, so you can see which airports you've visited in a given timeframe.  You can even compute the distance you've traveled or download your flights into Google Earth.

The other is analysis *within* a flight.  All of the discussion above is in the context of your whole logbook - across multiple flights.  But if you attach telemetry data to your flight, you can do additional analysis - particularly if it comes from the aircraft itself and has more than just latitude/longitude information.  For example, many units will include oil temperatures, cylinder head temperatures, exhaust gas temperatures, fuel levels, true airspeed, and so forth.  

MyFlightbook supports a wide variety of telemetry formats, including KML and GPX, but CSV may be the most powerful because it's so extensible  and thus can include things like the engine data I just described.  You can read more here about the sorts of things that you can include, but the key thing is that when you attach telemetry to a flight, you'll see a paper clip icon next to it:

If you click that icon, you can pick up to two fields to chart against a 3rd field:

Better still, if the data does include latitude/longitude/time information, you can click on a sample and see on the map beneath the chart where you were when that particular event occurred.

Hopefully somewhere in all of the various tools I've described above, you'll be able to answer any question about your logbook.  If not, please let me know and I'll see what I can do!

Sunday, June 26, 2022

Custom Rating Progress

MyFlightbook has long tracked your progress towards the flight experience requirements of various ratings (113 of them at last count!).  But inevitably there are some that are useful to track but that are too narrow in application for me to create.  Over the years I've had a few requests to allow for custom ratings progress.

Today I've taken that feature out.

You can create your own custom ratings on the Training->Ratings Progress page, by selecting "Custom Ratings/Progress" from the top drop-down.  When you do this, you'll see an expandable section where you can create your own rating progress.

A bit of terminology in this context:

  • Milestone is a specific criteria to meet.  For example, "40 hours of dual instruction received in a tailwheel airplane".
  • A Rating here is not really a privilege per se, it's simply a term for something to achieve when you've met all of the specified criteria.  A rating is really nothing more than a named collection of milestones.
Milestones follow the basic model of specifying a threshold that must be met, the attribute that must meet that threshold, and any constraints on the flights that qualify towards that threshold.  The constraints, in turn, are determined by a saved search.  You can also use "All flights" as the constraint.

So in the example above ("40 hours of dual instruction received in a tailwheel airplane"), the threshold is 40, the attribute here is "dual instruction received", and the "in a tailwheel airplane" is encapsulated in a saved search.

A milestone also includes 3 more fields: a regulatory reference (if one exists; otherwise, you can simply use a number as a way of sorting milestones), a title, and an optional note.

An example

Here's a simple example.  Suppose I belong to a flying club that has a tailwheel aircraft, but because of insurance requirements, I can't fly it until I have at least 300 hours of total time, at least 20 hours in tailwheel airplanes, and at least 10 hours of instruction in N6169M, the specific aircraft in question.

Step 1: Create the query for flights in N6169M.  Simple enough:

When I click "Find Matching Flights", this will be created as a query named "N6169M".  Pick any name you like, of course.

Repeat this to create a query for Tailwheel airplanes.  In my case, I'm going to name it "Tailwheel"

Step 2: Create the new rating.

Go to Training->Ratings Progress and choose "Custom Ratings/Progress, then expand the section to create a rating and fill it in:

Notice in the image above where it offers a space to fill in an optional longer description or notes; I'll show below where this appears.  For this example, I'm going to put in "This is an insurance requirement, not a regulatory requirement".  Click "Add a new rating" to create the rating and it will be available from the drop-down that contains the available custom ratings:

Notice where the note appears.  But alas, this isn't very useful yet: it has 0 milestones.  Let's create them.

Step 3: Create the milestones for the rating
Click on "Add/Edit Milestones" next to the rating:

You'll notice at the bottom of this screen that there are no milestones yet.  As we add milestones, they will appear there.

Since there's no actual regulatory requirement for this "rating", I'm just going to use 1, 2, and 3 as the Reference.

The 300 hours Total Time requirement is straightforward:

Notice that I can use simple markdown here for legibility: by surrounding words with asterisks, they will be in boldface when displayed.  You can also add hyperlinks by using the syntax "[Google](" to get it to display as Google. In this case, I added a link to the insurance documents to the note for this milestone.

Also notice that the first two milestones use "(All flights)" as the matching criteria.  When you click "Add new milestone/criteria", it shows up in the list of existing milestones:
You can add the other two milestones in the same way, choosing "Tailwheel" and "N6169M" as the "For flights matching" option as appropriate:

Note that if you make a mistake, you can click the red "x" next to a milestone and re-create it.

Step 4: Try it out

Close the milestone box above by clicking the gray "x" in the upper right corner, and you can select your custom currency from the list:

(Darn, I have all the experience but haven't been able to fly N6169M yet!  Looking forward to it...)

Limitations of Custom Ratings

Regulation-driven rating requirements tend to be complicated, and for that reason I've hand coded all of the more than 100 built-in ratings in the system.  The most common complications include things like substitutions  - for example, requiring a certain number of hours of flight experience, but allowing substitution of simulator time up to some limit.  Custom ratings progress do not possess these sorts of complications.

One other common requirement for regulatory ratings are "one-off" requirements, such as a long solo cross-country flight that is at least some distance.  Easy for me to hand-code, but getting that into a custom rating regime is...harder, so I've passed on doing so for now.

Interestingly, one other wrinkle in regulation driven requirements (at least for the FAA) is that some of them can decay over time.  Specifically, the FAA often requires a certain amount of training within a particular window prior to a checkride (e.g., 3 hours of training within the 2 calendar months preceding a checkride).  Because Saved Searches can handle these sorts of date windows, custom ratings can also encapsulate these!

But the vast majority of these sorts of things are simple requirements that follow the basic template of "Make sure you have [X hours or count, as appropriate] of [Some type of experience - total time, solo time, night flight, carrier landings] in flights that meet [some criteria]", and thus can be handled by custom ratings.

Please let me know if there are scenarios I'm not handling besides the ones above.