+static bool autoAlignLocalizers = false;
+static double autoAlignThreshold = 0.0;
+
+/**
+ * Given a runway, and proposed localiser data (ident, positioned and heading),
+ * precisely align the localiser with the actual runway heading, providing the
+ * difference between the localiser course and runway heading is less than a
+ * threshold. (To allow for localizers such as Kai-Tek requiring a turn on final).
+ *
+ * The positioned and heading argument are modified if changes are made.
+ */
+void alignLocaliserWithRunway(FGRunway* rwy, const string& ident, SGGeod& pos, double& heading)
+{
+ assert(rwy);
+ // find the distance from the threshold to the localizer
+ double dist = SGGeodesy::distanceM(pos, rwy->threshold());
+
+ // back project that distance along the runway center line
+ SGGeod newPos = rwy->pointOnCenterline(dist);
+
+ double hdg_diff = heading - rwy->headingDeg();
+ SG_NORMALIZE_RANGE(hdg_diff, -180.0, 180.0);
+
+ if ( fabs(hdg_diff) <= autoAlignThreshold ) {
+ pos = SGGeod::fromGeodFt(newPos, pos.getElevationFt());
+ heading = rwy->headingDeg();
+ } else {
+ SG_LOG(SG_NAVAID, SG_DEBUG, "localizer:" << ident << ", aligning with runway "
+ << rwy->ident() << " exceeded heading threshold");
+ }
+}
+
+static double defaultNavRange(const string& ident, FGPositioned::Type type)
+{
+ // Ranges are included with the latest data format, no need to
+ // assign our own defaults, unless the range is not set for some
+ // reason.
+ SG_LOG(SG_NAVAID, SG_DEBUG, "navaid " << ident << " has no range set, using defaults");
+ switch (type) {
+ case FGPositioned::NDB:
+ case FGPositioned::VOR:
+ return FG_NAV_DEFAULT_RANGE;
+
+ case FGPositioned::LOC:
+ case FGPositioned::ILS:
+ case FGPositioned::GS:
+ return FG_LOC_DEFAULT_RANGE;
+
+ case FGPositioned::DME: return FG_DME_DEFAULT_RANGE;
+ default: return FG_LOC_DEFAULT_RANGE;
+ }
+}
+
+
+namespace flightgear
+{
+
+static PositionedID readNavFromStream(std::istream& aStream,
+ FGPositioned::Type type = FGPositioned::INVALID)