Jump to content

[1.12.x] Kramax Autopilot Continued: Course guidance and auto-land for spaceplanes


linuxgurugamer

Recommended Posts

Has anyone made any kramax flightplans for the new Kerbinside Remastered?  It's a completely new mod which replaces the old KerbinSide (which still has some fairly catastrophic bugs in 1.4.5; the Remastered version is a lot better and has cool stuff like proper runway numbering, better assets, makes better use of Konstructs, etc).

Anyway, the author changed a bunch of airports, removed a bunch, added a bunch... the only kramax plans that are still workable are for KSC and the Island. 

I'm considering writing a bunch for this myself, but before I do that, has anyone else done that/is anyone else working on it?  I'm thinking I will write some python to generate these plans, and I will share the code.. ie, pass the python script the lat/lon/alt of the runway threshhold, runway heading, distance to FAF, IAF, etc, glideslope angle (default to 3), and it'll create a workable approach in the format described in this thread... can add other STAR waypoints as you wish, but this would be an easy way to procedurally generate approaches for any airport.

So, has anyone done any of this already?  i don't want to reinvent the wheel if I don't have to.

Link to comment
Share on other sites

@ss8913 I think a script for generating flight plans would be an excellent idea and I encourage you to proceed with this.

At one time, I created an excel spreadsheet to calculate the various points from lat/lon/alt input, but I didn't get it to a point where it was in a releasable form.

I exclusively use the Real Solar System mod. Therefore, none of the available Kerbin airports are valid. I've made quite a few flight plans for real airports around the world, but I am constantly wanting to add new ones.

Good luck!

Edited by Observe
Link to comment
Share on other sites

On 8/28/2018 at 12:24 PM, ss8913 said:

Has anyone made any kramax flightplans for the new Kerbinside Remastered?  It's a completely new mod which replaces the old KerbinSide (which still has some fairly catastrophic bugs in 1.4.5; the Remastered version is a lot better and has cool stuff like proper runway numbering, better assets, makes better use of Konstructs, etc).

Anyway, the author changed a bunch of airports, removed a bunch, added a bunch... the only kramax plans that are still workable are for KSC and the Island. 

I'm considering writing a bunch for this myself, but before I do that, has anyone else done that/is anyone else working on it?  I'm thinking I will write some python to generate these plans, and I will share the code.. ie, pass the python script the lat/lon/alt of the runway threshhold, runway heading, distance to FAF, IAF, etc, glideslope angle (default to 3), and it'll create a workable approach in the format described in this thread... can add other STAR waypoints as you wish, but this would be an easy way to procedurally generate approaches for any airport.

So, has anyone done any of this already?  i don't want to reinvent the wheel if I don't have to.

Also note that the slew of new runways, etc. are from @AdmiralTigerclaw work/submod, and separate from the Main KSR release.

I'd love to have access to all the new locations in KSR. Maybe the vectors for the runways/airports add-on can be obtained from that mod, too.

Nobody has done this yet, that I know of! :D

Link to comment
Share on other sites

ok.  yeah, the formulas I plan to use take into account the planetary radius.. calculates greatcircle distances; so it should work on other planets too if you just change the value of R.. I'll let you know when I have code ready.

Link to comment
Share on other sites

On 8/30/2018 at 7:25 PM, ss8913 said:

ok.  yeah, the formulas I plan to use take into account the planetary radius.. calculates greatcircle distances; so it should work on other planets too if you just change the value of R.. I'll let you know when I have code ready.

er.. with the distances involved, it might be simpler to use simple pythagorean equations and trigonometry (ie, assume the planet is flat since we're not dealing with a terribly large bit of it)

Link to comment
Share on other sites

  • 4 weeks later...

OK so... as I alluded to a month or two ago.. I made a python script which will take a runway threshhold lat/lon, plus a runway heading, and a few other parameters, and generate a kramax flightplan for the approach.  I have completed version 1.0.0 and I think it works.  I haven't fully tested it yet.

I may make a github page for it.  I'm going to post it here for y'all to use and comment on.  I'm releasing it as BSD license which means you can do literally whatever you want with it as long as you give me credit for the original version.  To the maintainers of this mod; feel free to package this with the mod if you wish.

should be able to just copy and paste the below code into a file, make it executable, and run it, at least on linux.  It'll likely work on windows too, if you have python for windows installed.  Developed with python 2.7.x, might not work on python 3, but it might, I didn't try that.

Without debug [-d] specified, it will spit out a block of text which can be copy-pasted into your FlightPlans.cfg in the mod's data directory.  Give it an argument of -h for documentation on commandline options.

 

#!/usr/bin/python
#
# fpgen.py
# KSP/Kramax Autopilot flightplan generator
# (C) 2018 Scott Stone <[email protected]>
#
# give this a runway threshhold lat/lon in decimal, plus a runway heading
# it will make an approach flightplan.  Defaults can be overridden on the
# commandline.
#
# haversine formulas courtesy of: https://www.movable-type.co.uk/scripts/latlong.html
# other trig formula courtesy of 11th grade math
#

import sys
import math
import argparse

### DEFAULTS

# kerbals use metric, distances are in km
# distances are from the runway threshhold, total not incremental
_FAF_DISTANCE = 13
_IAF_DISTANCE = 23
_APP1_DISTANCE = 50
_APP2_DISTANCE = 100
_APP3_DISTANCE = 200
_FAF_ALTITUDE = -1
_IAF_ALTITUDE = -1
_APP1_ALTITUDE = 6500
_APP2_ALTITUDE = 10000
_APP3_ALTITUDE = 12000
# glideslope in degrees
_GS = 4
# planetary radius in km, default is 600 for Kerbin
_PLANET = "Kerbin"
_RADIUS = 600
# fix names
fix_names = {
    'stop': 'STOP',
    'rwy': 'FLARE',
    'faf': 'FAF',
    'iaf': 'IAF',
    'app1': 'LOKEY',
    'app2': 'HIKEY',
    'app3': 'MAXKY'
}

# general options
SCRIPT_NAME = "fpgen"
SCRIPT_VERSION = "1.0.0"
SCRIPT_AUTHOR = "Scott Stone <[email protected]>"
SCRIPT_COPYRIGHT = "(C) 2018 Scott Stone - distributed under BSD License"
DEBUG = 0
NEWLINEFORMAT = "unix"


### SUBROUTINES

def dbg(msg):
    if (DEBUG > 0):
        sys.stderr.write("DEBUG: %s\n" % (msg))


def log(msg):
    newline = "\n"
    if (NEWLINEFORMAT != "unix"):
        newline = "\r\n"
    sys.stdout.write("%s%s" % (msg, newline))


# this function does some of the work
# uses the law of sines to find the altitude based on the glideslope angle
# ( sin alpha / a ) == ( sin beta / b )
# or in practical terms:
# ( sin glideslope / altitude) == ( sin topangle / distance )
# sin glideslope == ( altitude * sin topangle ) / distance
# distance * sin glideslope == altitude * sin topangle
# (distance * sin glideslope) / sin topangle == altitude
def calculateAltitude(_dist, slopeAngle):
    # _dist is in km, we need m
    dist = (_dist * 1000)
    topAngle = float(180 - (90 + slopeAngle))
    altitude = ((float(dist) * math.sin(math.radians(slopeAngle))) / (math.sin(math.radians(topAngle))))
    return altitude


# this function does most of the work
def haversine(key, lat, lon, hdg, dist, appdata):
    ad = float(float(dist) / float(appdata['radius']))
    lat2 = math.asin((math.sin(float(lat)) * math.cos(ad)) +
                     (math.cos(lat) * math.sin(ad) * math.cos(math.radians(hdg))))
    lon2 = lon + (math.atan2(math.sin(math.radians(hdg)) * math.sin(ad) * math.cos(lat),
                             math.cos(ad) - math.sin(lat) * math.sin(lat2)))
    rd = dict()
    rd['lat'] = lat2
    rd['lon'] = lon2
    rd['dist'] = dist
    rd['name'] = fix_names[key]
    # get altitudes from pythagorean theorm using glideslope for iaf to faf to runway else static
    altKey = "%s_altitude" % (key)
    if (int(appdata[altKey]) != -1):
        rd['alt'] = int(appdata[altKey])
    else:
        rd['alt'] = (calculateAltitude(dist, float(appdata['glideslope'])) +
                     int(appdata['rwy_altitude']))
    return rd


def logFlightPlan(appdata):
    log("KramaxAutoPilotPlans")
    log("{")
    log("    %s" % (appdata['planet']))
    log("    {")
    log("        FlightPlan")
    log("        {")
    log("            planet = %s" % (appdata['planet']))
    log("            name = %s" % (appdata['fpname']))
    log("            description = %s" % (appdata['fpdesc']))
    log("            WayPoints")
    log("            {")
    for k in ['app3', 'app2', 'app1', 'iaf', 'faf', 'rwy', 'stop']:
        log("                WayPoint")
        log("                {")
        log("                    Vertical = true")
        if (k == "iaf"):
            log("                    IAF = true")
        if (k == "faf"):
            log("                    FAF = true")
        if (k == "rwy"):
            log("                    RW = true")
        if (k == "stop"):
            log("                    Stop = true")
        log("                    lat = %.8f" % (appdata[k]['lat']))
        log("                    lon = %.8f" % (appdata[k]['lon']))
        log("                    alt = %d" % (appdata[k]['alt']))
        log("                    name = %s" % (fix_names[k]))
        log("                }")
    log("            }")
    log("        }")
    log("    }")
    log("}")


### MAIN EXECUTION

# dict to store our configuration values
approachData = dict()

# commandline args
parser = argparse.ArgumentParser()
parser.add_argument("-d", "--debug", help="Enable debugging", action="store_true")
parser.add_argument("-p", "--planet", help="Planet Name (dfl: %s)" % (_PLANET), default=_PLANET)
parser.add_argument("-g", "--glideslope", default=_GS, help="Specify glideslope in degrees, dfl %.2f" % (_GS))
parser.add_argument("-n", "--newlineformat", help="dos or unix newlines.  dfl: unix", default="unix")
parser.add_argument("--name", help="Flight Plan Name (required)")
parser.add_argument("--description", help="Flight Plan Description (required)")
parser.add_argument("--latitude", help="Specify rwy latitude (required)")
parser.add_argument("--longitude", help="Specify rwy longitude (required)")
parser.add_argument("--heading", help="Runway heading in degrees (required)")
parser.add_argument("-r", "--radius", default=_RADIUS, help="Planetary radius in km (dfl: %.2f)" % (_RADIUS))
parser.add_argument("--faf", default=_FAF_DISTANCE, help="FAF distance in km (dfl: %.2f)" % (_FAF_DISTANCE))
parser.add_argument("--iaf", default=_IAF_DISTANCE, help="IAF distance in km (dfl: %.2f)" % (_IAF_DISTANCE))
parser.add_argument("--app1", default=_APP1_DISTANCE, help="APP1 distance in km (dfl: %.2f)" % (_APP1_DISTANCE))
parser.add_argument("--app2", default=_APP2_DISTANCE, help="APP2 distance in km (dfl: %.2f)" % (_APP2_DISTANCE))
parser.add_argument("--app3", default=_APP3_DISTANCE, help="APP3 distance in km (dfl: %.2f)" % (_APP3_DISTANCE))
parser.add_argument("-a", "--altitude", help="Runway altitude in meters MSL (required)")
parser.add_argument("--fafalt", default=_FAF_ALTITUDE, help="FAF Altitude in m (dfl: %.2f)" % (_FAF_ALTITUDE))
parser.add_argument("--iafalt", default=_IAF_ALTITUDE, help="IAF Altitude in m (dfl: %.2f)" % (_IAF_ALTITUDE))
parser.add_argument("--app1alt", default=_APP1_ALTITUDE, help="APP1 Altitude in m (dfl: %.2f)" % (_APP1_ALTITUDE))
parser.add_argument("--app2alt", default=_APP2_ALTITUDE, help="APP2 Altitude in m (dfl: %.2f)" % (_APP2_ALTITUDE))
parser.add_argument("--app3alt", default=_APP3_ALTITUDE, help="APP3 Altitude in m (dfl: %.2f)" % (_APP3_ALTITUDE))

args = parser.parse_args()

if (args.debug):
    DEBUG = 1

if (
        (not args.latitude) or
        (not args.name) or
        (not args.description) or
        (not args.longitude) or
        (not args.heading) or
        (not args.altitude)):
    print "Name/Description/Lat/Lon/Heading/Altitude are required arguments.  Run with -h for help."
    sys.exit(1)

NEWLINEFORMAT = args.newlineformat
approachData['planet'] = args.planet
approachData['fpname'] = args.name
approachData['fpdesc'] = args.description
approachData['glideslope'] = args.glideslope or _GS
approachData['latitude'] = args.latitude
approachData['longitude'] = args.longitude
approachData['rwy_heading'] = args.heading
approachData['rwy_altitude'] = args.altitude
approachData['stop_altitude'] = args.altitude
approachData['radius'] = args.radius
approachData['faf_distance'] = args.faf
approachData['iaf_distance'] = args.iaf
approachData['app1_distance'] = args.app1
approachData['app2_distance'] = args.app2
approachData['app3_distance'] = args.app3
approachData['faf_altitude'] = args.fafalt
approachData['iaf_altitude'] = args.iafalt
approachData['app1_altitude'] = args.app1alt
approachData['app2_altitude'] = args.app2alt
approachData['app3_altitude'] = args.app3alt

if (DEBUG > 0):
    print approachData

reverse_heading = (float(approachData['rwy_heading']) + 180) % 360
approachData['stop'] = haversine('stop',
                                 float(approachData['latitude']),
                                 float(approachData['longitude']),
                                 float(approachData['rwy_heading']),
                                 1.0,
                                 approachData)
approachData['rwy'] = haversine('rwy',
                                float(approachData['latitude']),
                                float(approachData['longitude']),
                                float(approachData['rwy_heading']),
                                0.0,
                                approachData)
approachData['faf'] = haversine('faf',
                                float(approachData['latitude']),
                                float(approachData['longitude']),
                                reverse_heading,
                                float(approachData['faf_distance']),
                                approachData)
approachData['iaf'] = haversine('iaf',
                                float(approachData['latitude']),
                                float(approachData['longitude']),
                                reverse_heading,
                                float(approachData['iaf_distance']),
                                approachData)
approachData['app1'] = haversine('app1',
                                 float(approachData['latitude']),
                                 float(approachData['longitude']),
                                 reverse_heading,
                                 float(approachData['app1_distance']),
                                 approachData)
approachData['app2'] = haversine('app2',
                                 float(approachData['latitude']),
                                 float(approachData['longitude']),
                                 reverse_heading,
                                 float(approachData['app2_distance']),
                                 approachData)
approachData['app3'] = haversine('app3',
                                 float(approachData['latitude']),
                                 float(approachData['longitude']),
                                 reverse_heading,
                                 float(approachData['app3_distance']),
                                 approachData)

if (DEBUG > 0):
    print approachData

    for k in ['app3', 'app2', 'app1', 'iaf', 'faf', 'rwy', 'stop']:
        print "%s ::" % (k)
        print "    Name :: %s" % (approachData[k]['name'])
        print "     Lat :: %.4f" % (float(approachData[k]['lat']))
        print "     Lon :: %.4f" % (float(approachData[k]['lon']))
        print "    Dist :: %.4f km" % (float(approachData[k]['dist']))
        print "     Alt :: %.4f m" % (float(approachData[k]['alt']))
        print ""

logFlightPlan(approachData)


 

Edited by ss8913
used proper code formatting :)
Link to comment
Share on other sites

17 minutes ago, ss8913 said:

...I made a python script....

I think you should edit your post to put the code inside a code block ("<>" tool) inside a spoiler ("eye" tool) so it's not so long on the page.  Like what I have done below.

Spoiler

#!/usr/bin/python
#
# fpgen.py
# KSP/Kramax Autopilot flightplan generator
# (C) 2018 Scott Stone <ss8913@gmail.com>
#
# give this a runway threshhold lat/lon in decimal, plus a runway heading
# it will make an approach flightplan.  Defaults can be overridden on the
# commandline.
#
# haversine formulas courtesy of: https://www.movable-type.co.uk/scripts/latlong.html
# other trig formula courtesy of 11th grade math
#

import sys
import math
import argparse

### DEFAULTS

# kerbals use metric, distances are in km
# distances are from the runway threshhold, total not incremental
_FAF_DISTANCE = 13
_IAF_DISTANCE = 23
_APP1_DISTANCE = 50
_APP2_DISTANCE = 100
_APP3_DISTANCE = 200
_FAF_ALTITUDE = -1
_IAF_ALTITUDE = -1
_APP1_ALTITUDE = 6500
_APP2_ALTITUDE = 10000
_APP3_ALTITUDE = 12000
# glideslope in degrees
_GS = 4
# planetary radius in km, default is 600 for Kerbin
_PLANET = "Kerbin"
_RADIUS = 600
# fix names
fix_names = {
    'stop': 'STOP',
    'rwy': 'FLARE',
    'faf': 'FAF',
    'iaf': 'IAF',
    'app1': 'LOKEY',
    'app2': 'HIKEY',
    'app3': 'MAXKY'
}

# general options
SCRIPT_NAME = "fpgen"
SCRIPT_VERSION = "1.0.0"
SCRIPT_AUTHOR = "Scott Stone <ss8913@gmail.com>"
SCRIPT_COPYRIGHT = "(C) 2018 Scott Stone - distributed under BSD License"
DEBUG = 0
NEWLINEFORMAT = "unix"


### SUBROUTINES

def dbg(msg):
    if (DEBUG > 0):
        sys.stderr.write("DEBUG: %s\n" % (msg))


def log(msg):
    newline = "\n"
    if (NEWLINEFORMAT != "unix"):
        newline = "\r\n"
    sys.stdout.write("%s%s" % (msg, newline))


# this function does some of the work
# uses the law of sines to find the altitude based on the glideslope angle
# ( sin alpha / a ) == ( sin beta / b )
# or in practical terms:
# ( sin glideslope / altitude) == ( sin topangle / distance )
# sin glideslope == ( altitude * sin topangle ) / distance
# distance * sin glideslope == altitude * sin topangle
# (distance * sin glideslope) / sin topangle == altitude
def calculateAltitude(_dist, slopeAngle):
    # _dist is in km, we need m
    dist = (_dist * 1000)
    topAngle = float(180 - (90 + slopeAngle))
    altitude = ((float(dist) * math.sin(math.radians(slopeAngle))) / (math.sin(math.radians(topAngle))))
    return altitude


# this function does most of the work
def haversine(key, lat, lon, hdg, dist, appdata):
    ad = float(float(dist) / float(appdata['radius']))
    lat2 = math.asin((math.sin(float(lat)) * math.cos(ad)) +
                     (math.cos(lat) * math.sin(ad) * math.cos(math.radians(hdg))))
    lon2 = lon + (math.atan2(math.sin(math.radians(hdg)) * math.sin(ad) * math.cos(lat),
                             math.cos(ad) - math.sin(lat) * math.sin(lat2)))
    rd = dict()
    rd['lat'] = lat2
    rd['lon'] = lon2
    rd['dist'] = dist
    rd['name'] = fix_names[key]
    # get altitudes from pythagorean theorm using glideslope for iaf to faf to runway else static
    altKey = "%s_altitude" % (key)
    if (int(appdata[altKey]) != -1):
        rd['alt'] = int(appdata[altKey])
    else:
        rd['alt'] = (calculateAltitude(dist, float(appdata['glideslope'])) +
                     int(appdata['rwy_altitude']))
    return rd


def logFlightPlan(appdata):
    log("KramaxAutoPilotPlans")
    log("{")
    log("    %s" % (appdata['planet']))
    log("    {")
    log("        FlightPlan")
    log("        {")
    log("            planet = %s" % (appdata['planet']))
    log("            name = %s" % (appdata['fpname']))
    log("            description = %s" % (appdata['fpdesc']))
    log("            WayPoints")
    log("            {")
    for k in ['app3', 'app2', 'app1', 'iaf', 'faf', 'rwy', 'stop']:
        log("                WayPoint")
        log("                {")
        log("                    Vertical = true")
        if (k == "iaf"):
            log("                    IAF = true")
        if (k == "faf"):
            log("                    FAF = true")
        if (k == "rwy"):
            log("                    RW = true")
        if (k == "stop"):
            log("                    Stop = true")
        log("                    lat = %.8f" % (appdata[k]['lat']))
        log("                    lon = %.8f" % (appdata[k]['lon']))
        log("                    alt = %d" % (appdata[k]['alt']))
        log("                    name = %s" % (fix_names[k]))
        log("                }")
    log("            }")
    log("        }")
    log("    }")
    log("}")


### MAIN EXECUTION

# dict to store our configuration values
approachData = dict()

# commandline args
parser = argparse.ArgumentParser()
parser.add_argument("-d", "--debug", help="Enable debugging", action="store_true")
parser.add_argument("-p", "--planet", help="Planet Name (dfl: %s)" % (_PLANET), default=_PLANET)
parser.add_argument("-g", "--glideslope", default=_GS, help="Specify glideslope in degrees, dfl %.2f" % (_GS))
parser.add_argument("-n", "--newlineformat", help="dos or unix newlines.  dfl: unix", default="unix")
parser.add_argument("--name", help="Flight Plan Name (required)")
parser.add_argument("--description", help="Flight Plan Description (required)")
parser.add_argument("--latitude", help="Specify rwy latitude (required)")
parser.add_argument("--longitude", help="Specify rwy longitude (required)")
parser.add_argument("--heading", help="Runway heading in degrees (required)")
parser.add_argument("-r", "--radius", default=_RADIUS, help="Planetary radius in km (dfl: %.2f)" % (_RADIUS))
parser.add_argument("--faf", default=_FAF_DISTANCE, help="FAF distance in km (dfl: %.2f)" % (_FAF_DISTANCE))
parser.add_argument("--iaf", default=_IAF_DISTANCE, help="IAF distance in km (dfl: %.2f)" % (_IAF_DISTANCE))
parser.add_argument("--app1", default=_APP1_DISTANCE, help="APP1 distance in km (dfl: %.2f)" % (_APP1_DISTANCE))
parser.add_argument("--app2", default=_APP2_DISTANCE, help="APP2 distance in km (dfl: %.2f)" % (_APP2_DISTANCE))
parser.add_argument("--app3", default=_APP3_DISTANCE, help="APP3 distance in km (dfl: %.2f)" % (_APP3_DISTANCE))
parser.add_argument("-a", "--altitude", help="Runway altitude in meters MSL (required)")
parser.add_argument("--fafalt", default=_FAF_ALTITUDE, help="FAF Altitude in m (dfl: %.2f)" % (_FAF_ALTITUDE))
parser.add_argument("--iafalt", default=_IAF_ALTITUDE, help="IAF Altitude in m (dfl: %.2f)" % (_IAF_ALTITUDE))
parser.add_argument("--app1alt", default=_APP1_ALTITUDE, help="APP1 Altitude in m (dfl: %.2f)" % (_APP1_ALTITUDE))
parser.add_argument("--app2alt", default=_APP2_ALTITUDE, help="APP2 Altitude in m (dfl: %.2f)" % (_APP2_ALTITUDE))
parser.add_argument("--app3alt", default=_APP3_ALTITUDE, help="APP3 Altitude in m (dfl: %.2f)" % (_APP3_ALTITUDE))

args = parser.parse_args()

if (args.debug):
    DEBUG = 1

if (
        (not args.latitude) or
        (not args.name) or
        (not args.description) or
        (not args.longitude) or
        (not args.heading) or
        (not args.altitude)):
    print "Name/Description/Lat/Lon/Heading/Altitude are required arguments.  Run with -h for help."
    sys.exit(1)

NEWLINEFORMAT = args.newlineformat
approachData['planet'] = args.planet
approachData['fpname'] = args.name
approachData['fpdesc'] = args.description
approachData['glideslope'] = args.glideslope or _GS
approachData['latitude'] = args.latitude
approachData['longitude'] = args.longitude
approachData['rwy_heading'] = args.heading
approachData['rwy_altitude'] = args.altitude
approachData['stop_altitude'] = args.altitude
approachData['radius'] = args.radius
approachData['faf_distance'] = args.faf
approachData['iaf_distance'] = args.iaf
approachData['app1_distance'] = args.app1
approachData['app2_distance'] = args.app2
approachData['app3_distance'] = args.app3
approachData['faf_altitude'] = args.fafalt
approachData['iaf_altitude'] = args.iafalt
approachData['app1_altitude'] = args.app1alt
approachData['app2_altitude'] = args.app2alt
approachData['app3_altitude'] = args.app3alt

if (DEBUG > 0):
    print approachData

reverse_heading = (float(approachData['rwy_heading']) + 180) % 360
approachData['stop'] = haversine('stop',
                                 float(approachData['latitude']),
                                 float(approachData['longitude']),
                                 float(approachData['rwy_heading']),
                                 1.0,
                                 approachData)
approachData['rwy'] = haversine('rwy',
                                float(approachData['latitude']),
                                float(approachData['longitude']),
                                float(approachData['rwy_heading']),
                                0.0,
                                approachData)
approachData['faf'] = haversine('faf',
                                float(approachData['latitude']),
                                float(approachData['longitude']),
                                reverse_heading,
                                float(approachData['faf_distance']),
                                approachData)
approachData['iaf'] = haversine('iaf',
                                float(approachData['latitude']),
                                float(approachData['longitude']),
                                reverse_heading,
                                float(approachData['iaf_distance']),
                                approachData)
approachData['app1'] = haversine('app1',
                                 float(approachData['latitude']),
                                 float(approachData['longitude']),
                                 reverse_heading,
                                 float(approachData['app1_distance']),
                                 approachData)
approachData['app2'] = haversine('app2',
                                 float(approachData['latitude']),
                                 float(approachData['longitude']),
                                 reverse_heading,
                                 float(approachData['app2_distance']),
                                 approachData)
approachData['app3'] = haversine('app3',
                                 float(approachData['latitude']),
                                 float(approachData['longitude']),
                                 reverse_heading,
                                 float(approachData['app3_distance']),
                                 approachData)

if (DEBUG > 0):
    print approachData

    for k in ['app3', 'app2', 'app1', 'iaf', 'faf', 'rwy', 'stop']:
        print "%s ::" % (k)
        print "    Name :: %s" % (approachData[k]['name'])
        print "     Lat :: %.4f" % (float(approachData[k]['lat']))
        print "     Lon :: %.4f" % (float(approachData[k]['lon']))
        print "    Dist :: %.4f km" % (float(approachData[k]['dist']))
        print "     Alt :: %.4f m" % (float(approachData[k]['alt']))
        print ""

logFlightPlan(approachData)

 

 

Link to comment
Share on other sites

3 hours ago, Jacke said:

I think you should edit your post to put the code inside a code block ("<>" tool) inside a spoiler ("eye" tool) so it's not so long on the page.  Like what I have done below.

  Reveal hidden contents


#!/usr/bin/python
#
# fpgen.py
# KSP/Kramax Autopilot flightplan generator
# (C) 2018 Scott Stone <[email protected]>
#
# give this a runway threshhold lat/lon in decimal, plus a runway heading
# it will make an approach flightplan.  Defaults can be overridden on the
# commandline.
#
# haversine formulas courtesy of: https://www.movable-type.co.uk/scripts/latlong.html
# other trig formula courtesy of 11th grade math
#

import sys
import math
import argparse

### DEFAULTS

# kerbals use metric, distances are in km
# distances are from the runway threshhold, total not incremental
_FAF_DISTANCE = 13
_IAF_DISTANCE = 23
_APP1_DISTANCE = 50
_APP2_DISTANCE = 100
_APP3_DISTANCE = 200
_FAF_ALTITUDE = -1
_IAF_ALTITUDE = -1
_APP1_ALTITUDE = 6500
_APP2_ALTITUDE = 10000
_APP3_ALTITUDE = 12000
# glideslope in degrees
_GS = 4
# planetary radius in km, default is 600 for Kerbin
_PLANET = "Kerbin"
_RADIUS = 600
# fix names
fix_names = {
    'stop': 'STOP',
    'rwy': 'FLARE',
    'faf': 'FAF',
    'iaf': 'IAF',
    'app1': 'LOKEY',
    'app2': 'HIKEY',
    'app3': 'MAXKY'
}

# general options
SCRIPT_NAME = "fpgen"
SCRIPT_VERSION = "1.0.0"
SCRIPT_AUTHOR = "Scott Stone <[email protected]>"
SCRIPT_COPYRIGHT = "(C) 2018 Scott Stone - distributed under BSD License"
DEBUG = 0
NEWLINEFORMAT = "unix"


### SUBROUTINES

def dbg(msg):
    if (DEBUG > 0):
        sys.stderr.write("DEBUG: %s\n" % (msg))


def log(msg):
    newline = "\n"
    if (NEWLINEFORMAT != "unix"):
        newline = "\r\n"
    sys.stdout.write("%s%s" % (msg, newline))


# this function does some of the work
# uses the law of sines to find the altitude based on the glideslope angle
# ( sin alpha / a ) == ( sin beta / b )
# or in practical terms:
# ( sin glideslope / altitude) == ( sin topangle / distance )
# sin glideslope == ( altitude * sin topangle ) / distance
# distance * sin glideslope == altitude * sin topangle
# (distance * sin glideslope) / sin topangle == altitude
def calculateAltitude(_dist, slopeAngle):
    # _dist is in km, we need m
    dist = (_dist * 1000)
    topAngle = float(180 - (90 + slopeAngle))
    altitude = ((float(dist) * math.sin(math.radians(slopeAngle))) / (math.sin(math.radians(topAngle))))
    return altitude


# this function does most of the work
def haversine(key, lat, lon, hdg, dist, appdata):
    ad = float(float(dist) / float(appdata['radius']))
    lat2 = math.asin((math.sin(float(lat)) * math.cos(ad)) +
                     (math.cos(lat) * math.sin(ad) * math.cos(math.radians(hdg))))
    lon2 = lon + (math.atan2(math.sin(math.radians(hdg)) * math.sin(ad) * math.cos(lat),
                             math.cos(ad) - math.sin(lat) * math.sin(lat2)))
    rd = dict()
    rd['lat'] = lat2
    rd['lon'] = lon2
    rd['dist'] = dist
    rd['name'] = fix_names[key]
    # get altitudes from pythagorean theorm using glideslope for iaf to faf to runway else static
    altKey = "%s_altitude" % (key)
    if (int(appdata[altKey]) != -1):
        rd['alt'] = int(appdata[altKey])
    else:
        rd['alt'] = (calculateAltitude(dist, float(appdata['glideslope'])) +
                     int(appdata['rwy_altitude']))
    return rd


def logFlightPlan(appdata):
    log("KramaxAutoPilotPlans")
    log("{")
    log("    %s" % (appdata['planet']))
    log("    {")
    log("        FlightPlan")
    log("        {")
    log("            planet = %s" % (appdata['planet']))
    log("            name = %s" % (appdata['fpname']))
    log("            description = %s" % (appdata['fpdesc']))
    log("            WayPoints")
    log("            {")
    for k in ['app3', 'app2', 'app1', 'iaf', 'faf', 'rwy', 'stop']:
        log("                WayPoint")
        log("                {")
        log("                    Vertical = true")
        if (k == "iaf"):
            log("                    IAF = true")
        if (k == "faf"):
            log("                    FAF = true")
        if (k == "rwy"):
            log("                    RW = true")
        if (k == "stop"):
            log("                    Stop = true")
        log("                    lat = %.8f" % (appdata[k]['lat']))
        log("                    lon = %.8f" % (appdata[k]['lon']))
        log("                    alt = %d" % (appdata[k]['alt']))
        log("                    name = %s" % (fix_names[k]))
        log("                }")
    log("            }")
    log("        }")
    log("    }")
    log("}")


### MAIN EXECUTION

# dict to store our configuration values
approachData = dict()

# commandline args
parser = argparse.ArgumentParser()
parser.add_argument("-d", "--debug", help="Enable debugging", action="store_true")
parser.add_argument("-p", "--planet", help="Planet Name (dfl: %s)" % (_PLANET), default=_PLANET)
parser.add_argument("-g", "--glideslope", default=_GS, help="Specify glideslope in degrees, dfl %.2f" % (_GS))
parser.add_argument("-n", "--newlineformat", help="dos or unix newlines.  dfl: unix", default="unix")
parser.add_argument("--name", help="Flight Plan Name (required)")
parser.add_argument("--description", help="Flight Plan Description (required)")
parser.add_argument("--latitude", help="Specify rwy latitude (required)")
parser.add_argument("--longitude", help="Specify rwy longitude (required)")
parser.add_argument("--heading", help="Runway heading in degrees (required)")
parser.add_argument("-r", "--radius", default=_RADIUS, help="Planetary radius in km (dfl: %.2f)" % (_RADIUS))
parser.add_argument("--faf", default=_FAF_DISTANCE, help="FAF distance in km (dfl: %.2f)" % (_FAF_DISTANCE))
parser.add_argument("--iaf", default=_IAF_DISTANCE, help="IAF distance in km (dfl: %.2f)" % (_IAF_DISTANCE))
parser.add_argument("--app1", default=_APP1_DISTANCE, help="APP1 distance in km (dfl: %.2f)" % (_APP1_DISTANCE))
parser.add_argument("--app2", default=_APP2_DISTANCE, help="APP2 distance in km (dfl: %.2f)" % (_APP2_DISTANCE))
parser.add_argument("--app3", default=_APP3_DISTANCE, help="APP3 distance in km (dfl: %.2f)" % (_APP3_DISTANCE))
parser.add_argument("-a", "--altitude", help="Runway altitude in meters MSL (required)")
parser.add_argument("--fafalt", default=_FAF_ALTITUDE, help="FAF Altitude in m (dfl: %.2f)" % (_FAF_ALTITUDE))
parser.add_argument("--iafalt", default=_IAF_ALTITUDE, help="IAF Altitude in m (dfl: %.2f)" % (_IAF_ALTITUDE))
parser.add_argument("--app1alt", default=_APP1_ALTITUDE, help="APP1 Altitude in m (dfl: %.2f)" % (_APP1_ALTITUDE))
parser.add_argument("--app2alt", default=_APP2_ALTITUDE, help="APP2 Altitude in m (dfl: %.2f)" % (_APP2_ALTITUDE))
parser.add_argument("--app3alt", default=_APP3_ALTITUDE, help="APP3 Altitude in m (dfl: %.2f)" % (_APP3_ALTITUDE))

args = parser.parse_args()

if (args.debug):
    DEBUG = 1

if (
        (not args.latitude) or
        (not args.name) or
        (not args.description) or
        (not args.longitude) or
        (not args.heading) or
        (not args.altitude)):
    print "Name/Description/Lat/Lon/Heading/Altitude are required arguments.  Run with -h for help."
    sys.exit(1)

NEWLINEFORMAT = args.newlineformat
approachData['planet'] = args.planet
approachData['fpname'] = args.name
approachData['fpdesc'] = args.description
approachData['glideslope'] = args.glideslope or _GS
approachData['latitude'] = args.latitude
approachData['longitude'] = args.longitude
approachData['rwy_heading'] = args.heading
approachData['rwy_altitude'] = args.altitude
approachData['stop_altitude'] = args.altitude
approachData['radius'] = args.radius
approachData['faf_distance'] = args.faf
approachData['iaf_distance'] = args.iaf
approachData['app1_distance'] = args.app1
approachData['app2_distance'] = args.app2
approachData['app3_distance'] = args.app3
approachData['faf_altitude'] = args.fafalt
approachData['iaf_altitude'] = args.iafalt
approachData['app1_altitude'] = args.app1alt
approachData['app2_altitude'] = args.app2alt
approachData['app3_altitude'] = args.app3alt

if (DEBUG > 0):
    print approachData

reverse_heading = (float(approachData['rwy_heading']) + 180) % 360
approachData['stop'] = haversine('stop',
                                 float(approachData['latitude']),
                                 float(approachData['longitude']),
                                 float(approachData['rwy_heading']),
                                 1.0,
                                 approachData)
approachData['rwy'] = haversine('rwy',
                                float(approachData['latitude']),
                                float(approachData['longitude']),
                                float(approachData['rwy_heading']),
                                0.0,
                                approachData)
approachData['faf'] = haversine('faf',
                                float(approachData['latitude']),
                                float(approachData['longitude']),
                                reverse_heading,
                                float(approachData['faf_distance']),
                                approachData)
approachData['iaf'] = haversine('iaf',
                                float(approachData['latitude']),
                                float(approachData['longitude']),
                                reverse_heading,
                                float(approachData['iaf_distance']),
                                approachData)
approachData['app1'] = haversine('app1',
                                 float(approachData['latitude']),
                                 float(approachData['longitude']),
                                 reverse_heading,
                                 float(approachData['app1_distance']),
                                 approachData)
approachData['app2'] = haversine('app2',
                                 float(approachData['latitude']),
                                 float(approachData['longitude']),
                                 reverse_heading,
                                 float(approachData['app2_distance']),
                                 approachData)
approachData['app3'] = haversine('app3',
                                 float(approachData['latitude']),
                                 float(approachData['longitude']),
                                 reverse_heading,
                                 float(approachData['app3_distance']),
                                 approachData)

if (DEBUG > 0):
    print approachData

    for k in ['app3', 'app2', 'app1', 'iaf', 'faf', 'rwy', 'stop']:
        print "%s ::" % (k)
        print "    Name :: %s" % (approachData[k]['name'])
        print "     Lat :: %.4f" % (float(approachData[k]['lat']))
        print "     Lon :: %.4f" % (float(approachData[k]['lon']))
        print "    Dist :: %.4f km" % (float(approachData[k]['dist']))
        print "     Alt :: %.4f m" % (float(approachData[k]['alt']))
        print ""

logFlightPlan(approachData)

 

good idea.  i'm not that familiar with all the functions of this particular forum software... I'll edit/repost

2

 

Link to comment
Share on other sites

@ss8913 I tried your script, but am unable to get it to work. Running Windows, I installed Python. Your script runs, but kicks out syntax errors. Admittedly, I have zero knowledge of Python code. Errors include missing parenthesis for Print. Adding parenthesis moves on to a new error. I wonder if you could test on Windows and also if you could provide example command line with arguments. Thanks.

Link to comment
Share on other sites

49 minutes ago, Observe said:

@ss8913 I tried your script, but am unable to get it to work. Running Windows, I installed Python. Your script runs, but kicks out syntax errors. Admittedly, I have zero knowledge of Python code. Errors include missing parenthesis for Print. Adding parenthesis moves on to a new error. I wonder if you could test on Windows and also if you could provide example command line with arguments. Thanks.

which version of python?  I did not test this on python 3, only on python 2.7, and I didn't test it on windows, either; python requires indentation for formatting and dos newlines might be an issue?  anyway let me know what version of python you're on and we'll debug it from there, perhaps there was a problem in the original paste as well, maybe I should put it up on dropbox for direct d/l or make a github project.

Link to comment
Share on other sites

@ss8913I downloaded current version 3.7.0 and I read version 3 requires parenthesis for Print command. After adding parenthesis for all incidences of Print, your script seems to work. Now I need to figure out how to pass the required arguments. I'm working on that now.

Could you post an example command-line with arguments. Thanks.

Link to comment
Share on other sites

@ss8913 Latitude calculation doesn't seem to be working. Here is my command:

python kramax.py -p="Earth" -r="6371000" --name="NZ East" --description="my flight" --latitude="-37.684254347674603" --longitude="178.54679267814799" --heading="90" --altitude="507"

Problem with latitude: Output gives "lat=0.014xx". Should be around -37.xx

KramaxAutoPilotPlans
{
    Earth
    {
        FlightPlan
        {
            planet = Earth
            name = NZ East
            description = my flight
            WayPoints
            {
                WayPoint
                {
                    Vertical = true
                    lat = 0.01482610
                    lon = 178.54679268
                    alt = 12000
                    name = MAXKY
                }
                WayPoint
                {
                    Vertical = true
                    lat = 0.01484180
                    lon = 178.54679268
                    alt = 10000
                    name = HIKEY
                }
                WayPoint
                {
                    Vertical = true
                    lat = 0.01484965
                    lon = 178.54679268
                    alt = 6500
                    name = LOKEY
                }
                WayPoint
                {
                    Vertical = true
                    IAF = true
                    lat = 0.01485389
                    lon = 178.54679268
                    alt = 2115
                    name = IAF
                }
                WayPoint
                {
                    Vertical = true
                    FAF = true
                    lat = 0.01485545
                    lon = 178.54679268
                    alt = 1416
                    name = FAF
                }
                WayPoint
                {
                    Vertical = true
                    RW = true
                    lat = 0.01485750
                    lon = 178.54679268
                    alt = 507
                    name = FLARE
                }
                WayPoint
                {
                    Vertical = true
                    Stop = true
                    lat = 0.01485765
                    lon = 178.54679268
                    alt = 507
                    name = STOP
                }
            }
        }
    }
}

 

Edited by Observe
Link to comment
Share on other sites

On 9/30/2018 at 1:14 PM, Observe said:

@ss8913 Latitude calculation doesn't seem to be working. Here is my command:


python kramax.py -p="Earth" -r="6371000" --name="NZ East" --description="my flight" --latitude="-37.684254347674603" --longitude="178.54679267814799" --heading="90" --altitude="507"

Problem with latitude: Output gives "lat=0.014xx". Should be around -37.xx


KramaxAutoPilotPlans
{
    Earth
    {
        FlightPlan
        {
            planet = Earth
            name = NZ East
            description = my flight
            WayPoints
            {
                WayPoint
                {
                    Vertical = true
                    lat = 0.01482610
                    lon = 178.54679268
                    alt = 12000
                    name = MAXKY
                }
                WayPoint
                {
                    Vertical = true
                    lat = 0.01484180
                    lon = 178.54679268
                    alt = 10000
                    name = HIKEY
                }
                WayPoint
                {
                    Vertical = true
                    lat = 0.01484965
                    lon = 178.54679268
                    alt = 6500
                    name = LOKEY
                }
                WayPoint
                {
                    Vertical = true
                    IAF = true
                    lat = 0.01485389
                    lon = 178.54679268
                    alt = 2115
                    name = IAF
                }
                WayPoint
                {
                    Vertical = true
                    FAF = true
                    lat = 0.01485545
                    lon = 178.54679268
                    alt = 1416
                    name = FAF
                }
                WayPoint
                {
                    Vertical = true
                    RW = true
                    lat = 0.01485750
                    lon = 178.54679268
                    alt = 507
                    name = FLARE
                }
                WayPoint
                {
                    Vertical = true
                    Stop = true
                    lat = 0.01485765
                    lon = 178.54679268
                    alt = 507
                    name = STOP
                }
            }
        }
    }
}

 

interesting... are you sure the radius is right?  or maybe my default radius is wrong and I tested it incorrectly.  I'll double check the math tonight.

 

interesting that a few parens was all that was needed to port to python3.  as I have stated, python2 and python3 are not directly compatible which is why I stressed that this was python2 code.

glad it was easy to convert for 3.

Edited by ss8913
Link to comment
Share on other sites

@ss8913My mistake on the radius. It should be 6371 for Earth (RSS mod). Nevertheless, the latitude is still completely wrong. Here are my parameters and output:

python kramax.py -p="Earth" -r="6371" --name="Auckland Intl" --description="Auckland International airport" --latitude="-36.995305687613502" --longitude="174.84381177178" --heading="88" --altitude="200"

KramaxAutoPilotPlans
{
    Earth
    {
        FlightPlan
        {
            planet = Earth
            name = Auckland Intl
            description = Auckland International airport
            WayPoints
            {
                WayPoint
                {
                    Vertical = true
                    lat = 0.70229352
                    lon = 174.80270837
                    alt = 12000
                    name = MAXKY
                }
                WayPoint
                {
                    Vertical = true
                    lat = 0.70315401
                    lon = 174.82324689
                    alt = 10000
                    name = HIKEY
                }
                WayPoint
                {
                    Vertical = true
                    lat = 0.70350616
                    lon = 174.83352649
                    alt = 6500
                    name = LOKEY
                }
                WayPoint
                {
                    Vertical = true
                    IAF = true
                    lat = 0.70367464
                    lon = 174.83907989
                    alt = 1808
                    name = IAF
                }
                WayPoint
                {
                    Vertical = true
                    FAF = true
                    lat = 0.70373318
                    lon = 174.84113710
                    alt = 1109
                    name = FAF
                }
                WayPoint
                {
                    Vertical = true
                    RW = true
                    lat = 0.70380616
                    lon = 174.84381177
                    alt = 200
                    name = FLARE
                }
                WayPoint
                {
                    Vertical = true
                    Stop = true
                    lat = 0.70381162
                    lon = 174.84401753
                    alt = 200
                    name = STOP
                }
            }
        }
    }
}

EDIT: I downloaded and installed Python version 2.7.15. Same results. Latitude wrong. Thanks.

Edited by Observe
Link to comment
Share on other sites

alrighty, yes, I was doing the math slightly off and not converting radians back to degrees.. and a few other things... this version works now :)  I tested it against the Kramax default ILS 09 plan and got roughly the same results (slightly different because I wasn't rounding things and just using a 4 degree glideslope; which I suppose is required to clear those mountains from MAXKY :) )

#!/usr/bin/python
#
# fpgen.py
# KSP/Kramax Autopilot flightplan generator
# (C) 2018 Scott Stone <[email protected]>
#
# give this a runway threshhold lat/lon in decimal, plus a runway heading
# it will make an approach flightplan.  Defaults can be overridden on the
# commandline.
#
# haversine formulas courtesy of: https://www.movable-type.co.uk/scripts/latlong.html
# other trig formula courtesy of 11th grade math
#
# THIS VERSION IS FOR PYTHON 2.x
# IT WILL NOT WORK AS-IS IN PYTHON 3.x
# I hear if you put parens around all the print functions, it will.
#

import sys
import math
import argparse
import re

### DEFAULTS

# kerbals use metric, distances are in km
# distances are from the runway threshhold, total not incremental
_FAF_DISTANCE = 13
_IAF_DISTANCE = 23
_APP1_DISTANCE = 50
_APP2_DISTANCE = 100
_APP3_DISTANCE = 200
_FAF_ALTITUDE = -1
_IAF_ALTITUDE = -1
_APP1_ALTITUDE = 6500
_APP2_ALTITUDE = 10000
_APP3_ALTITUDE = 12000
# flare height in meters
_FLARE = 25
# glideslope in degrees
_GS = 4
# planetary radius in km, default is 600 for Kerbin
_PLANET = "Kerbin"
_RADIUS = 600.000
# fix names
fix_names = {
    'stop': 'STOP',
    'rwy': 'FLARE',
    'faf': 'FAF',
    'iaf': 'IAF',
    'app1': 'LOKEY',
    'app2': 'HIKEY',
    'app3': 'MAXKY'
}

# general options
SCRIPT_NAME = "fpgen"
SCRIPT_VERSION = "1.1.0"
SCRIPT_AUTHOR = "Scott Stone <[email protected]>"
SCRIPT_COPYRIGHT = "(C) 2018 Scott Stone - distributed under BSD License"
DEBUG = 0
NEWLINEFORMAT = "unix"


### SUBROUTINES

def dbg(msg):
    if (DEBUG > 0):
        sys.stderr.write("DEBUG: %s\n" % (msg))


def log(msg):
    newline = "\n"
    if (NEWLINEFORMAT != "unix"):
        newline = "\r\n"
    sys.stdout.write("%s%s" % (msg, newline))


# this function does some of the work
# uses the law of sines to find the altitude based on the glideslope angle
# ( sin alpha / a ) == ( sin beta / b )
# or in practical terms:
# ( sin glideslope / altitude) == ( sin topangle / distance )
# sin glideslope == ( altitude * sin topangle ) / distance
# distance * sin glideslope == altitude * sin topangle
# (distance * sin glideslope) / sin topangle == altitude
def calculateAltitude(_dist, slopeAngle):
    # _dist is in km, we need m
    dist = (_dist * 1000)
    topAngle = float(180 - (90 + slopeAngle))
    altitude = ((float(dist) * math.sin(math.radians(slopeAngle))) / (math.sin(math.radians(topAngle))))
    return altitude


# radius given in km here
# lat/lon in signed decimal degrees
# returns a value in km
# TESTED: this function works properly
def haversineDistance(lat1, lon1, lat2, lon2, radius):
    dbg("haversineDistance received [%.4f / %.4f], [%.4f / %.4f], %.4f km radius" % (lat1, lon1, lat2, lon2, radius))
    R = (radius * 1000)
    phi_1 = math.radians(lat1)
    phi_2 = math.radians(lat2)
    delta_phi = math.radians(lat2 - lat1)
    delta_lambda = math.radians(lon2 - lon1)
    a = math.sin(delta_phi / 2.0) ** 2 + math.cos(phi_1) * math.cos(phi_2) * math.sin(delta_lambda / 2.0) ** 2
    c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
    d = R * c
    # get kilometers
    dkm = d / 1000.0
    return dkm


# this function does most of the work
def haversine(key, lat, lon, hdg, dist, appdata):
    # ad = float(float(dist) / float(appdata['radius']))
    radius1 = (float(appdata['radius']) * 1000)
    dist1 = (dist * 1000)
    # ad = math.radians(float(float(dist1) / float ( radius1 ) ) )
    ad = float(float(dist1) / float(radius1))
    lat1 = math.radians(float(lat))
    lon1 = math.radians(float(lon))
    hdg1 = math.radians(float(hdg))
    dbg("haversine function has %.4f/%.4f heading %.4f distance %.4f km" % (
        float(lat), float(lon), float(hdg), float(dist)))
    dbg("converted values %.8f/%.8f heading %.4f radians distance %.4f m, planetary radius %.4fm" % (
        lat1, lon1, hdg1, dist1, radius1))
    lat2 = math.asin((math.sin(lat1) * math.cos(ad)) + math.cos(lat1) * math.sin(ad) * math.cos(hdg1))
    lon2 = lon1 + math.atan2(math.sin(hdg1) * math.sin(ad) * math.cos(lat1),
                             math.cos(ad) - math.sin(lat1) * math.sin(lat2))
    rd = dict()
    rd['lat'] = math.degrees(lat2)
    rd['lon'] = math.degrees(lon2)
    rd['dist'] = dist
    rd['name'] = fix_names[key]
    # get altitudes from law of sines using glideslope for iaf to faf to runway else static
    altKey = "%s_altitude" % (key)
    if (int(appdata[altKey]) != -1):
        rd['alt'] = int(appdata[altKey])
    else:
        rd['alt'] = (calculateAltitude(dist, float(appdata['glideslope'])) +
                     int(appdata['rwy_altitude']))
    return rd


def logFlightPlan(appdata):
    log("KramaxAutoPilotPlans")
    log("{")
    log("    %s" % (appdata['planet']))
    log("    {")
    log("        FlightPlan")
    log("        {")
    log("            planet = %s" % (appdata['planet']))
    log("            name = %s" % (appdata['fpname']))
    log("            description = %s" % (appdata['fpdesc']))
    log("            WayPoints")
    log("            {")
    for k in ['app3', 'app2', 'app1', 'iaf', 'faf', 'rwy', 'stop']:
        log("                WayPoint")
        log("                {")
        log("                    Vertical = true")
        if (k == "iaf"):
            log("                    IAF = true")
        if (k == "faf"):
            log("                    FAF = true")
        if (k == "rwy"):
            log("                    RW = true")
        if (k == "stop"):
            log("                    Stop = true")
        log("                    lat = %.8f" % (appdata[k]['lat']))
        log("                    lon = %.8f" % (appdata[k]['lon']))
        log("                    alt = %d" % (appdata[k]['alt']))
        log("                    name = %s" % (fix_names[k]))
        log("                }")
    log("            }")
    log("        }")
    log("    }")
    log("}")


### MAIN EXECUTION

# dict to store our configuration values
approachData = dict()

# commandline args
parser = argparse.ArgumentParser()
parser.add_argument("-d", "--debug", help="Enable debugging", action="store_true")
parser.add_argument("-p", "--planet", help="Planet Name (dfl: %s)" % (_PLANET), default=_PLANET)
parser.add_argument("-g", "--glideslope", default=_GS, help="Specify glideslope in degrees, dfl %.2f" % (_GS))
parser.add_argument("-n", "--newlineformat", help="dos or unix newlines.  dfl: unix", default="unix")
parser.add_argument("--name", help="Flight Plan Name (required)")
parser.add_argument("--description", help="Flight Plan Description (required)")
parser.add_argument("--latitude", type=float, help="Specify rwy latitude (required)")
parser.add_argument("--longitude", type=float, help="Specify rwy longitude (required)")
parser.add_argument("--heading", type=float, help="Runway heading in degrees (required)")
parser.add_argument("-f", "--flare", type=int, default=_FLARE, help="Flare height in meters (dfl: %d)" % (_FLARE))
parser.add_argument("-r", "--radius", type=float, default=_RADIUS,
                    help="Planetary radius in km (dfl: %.2f)" % (_RADIUS))
parser.add_argument("--faf", default=_FAF_DISTANCE, help="FAF distance in km (dfl: %.2f)" % (_FAF_DISTANCE))
parser.add_argument("--iaf", default=_IAF_DISTANCE, help="IAF distance in km (dfl: %.2f)" % (_IAF_DISTANCE))
parser.add_argument("--app1", default=_APP1_DISTANCE, help="APP1 distance in km (dfl: %.2f)" % (_APP1_DISTANCE))
parser.add_argument("--app2", default=_APP2_DISTANCE, help="APP2 distance in km (dfl: %.2f)" % (_APP2_DISTANCE))
parser.add_argument("--app3", default=_APP3_DISTANCE, help="APP3 distance in km (dfl: %.2f)" % (_APP3_DISTANCE))
parser.add_argument("-a", "--altitude", type=int, help="Runway altitude in meters MSL (required)")
parser.add_argument("--fafalt", default=_FAF_ALTITUDE, help="FAF Altitude in m (dfl: %.2f)" % (_FAF_ALTITUDE))
parser.add_argument("--iafalt", default=_IAF_ALTITUDE, help="IAF Altitude in m (dfl: %.2f)" % (_IAF_ALTITUDE))
parser.add_argument("--app1alt", default=_APP1_ALTITUDE, help="APP1 Altitude in m (dfl: %.2f)" % (_APP1_ALTITUDE))
parser.add_argument("--app2alt", default=_APP2_ALTITUDE, help="APP2 Altitude in m (dfl: %.2f)" % (_APP2_ALTITUDE))
parser.add_argument("--app3alt", default=_APP3_ALTITUDE, help="APP3 Altitude in m (dfl: %.2f)" % (_APP3_ALTITUDE))
parser.add_argument("-D", "--distanceonly", help="Just calculate distance, X/Y,X/Y", action="store_true")
parser.add_argument("--dlat1", type=float, help="Distance calc latitude 1")
parser.add_argument("--dlon1", type=float, help="Distance calc longitude 1")
parser.add_argument("--dlat2", type=float, help="Distance calc latitude 2")
parser.add_argument("--dlon2", type=float, help="Distance calc longitude 2")

args = parser.parse_args()

if (args.debug):
    DEBUG = 1

if (args.distanceonly):
    dbg("distanceonly coords: [%.4f/%.4f %.4f/%.4f]" % (args.dlat1, args.dlon1, args.dlat2, args.dlon2))
    try:
        d = haversineDistance(args.dlat1, args.dlon1, args.dlat2, args.dlon2, float(args.radius))
        print "Distance is %.4f km" % (float(d))
    except:
        ea = sys.exc_info()
        log("ERROR: %s %s" % (ea[0], ea[1]))
        sys.exit(1)
    sys.exit(0)

if (
        (not args.latitude) or
        (not args.name) or
        (not args.description) or
        (not args.longitude) or
        (not args.heading) or
        (not args.altitude)):
    print "Name/Description/Lat/Lon/Heading/Altitude are required arguments.  Run with -h for help."
    sys.exit(1)

NEWLINEFORMAT = args.newlineformat
approachData['planet'] = args.planet
approachData['fpname'] = args.name
approachData['fpdesc'] = args.description
approachData['glideslope'] = args.glideslope or _GS
approachData['latitude'] = args.latitude
approachData['longitude'] = args.longitude
approachData['rwy_heading'] = args.heading
approachData['rwy_altitude'] = args.altitude + args.flare
approachData['stop_altitude'] = args.altitude
approachData['radius'] = args.radius
approachData['faf_distance'] = args.faf
approachData['iaf_distance'] = args.iaf
approachData['app1_distance'] = args.app1
approachData['app2_distance'] = args.app2
approachData['app3_distance'] = args.app3
approachData['faf_altitude'] = args.fafalt
approachData['iaf_altitude'] = args.iafalt
approachData['app1_altitude'] = args.app1alt
approachData['app2_altitude'] = args.app2alt
approachData['app3_altitude'] = args.app3alt

if (DEBUG > 0):
    print approachData

reverse_heading = (float(approachData['rwy_heading']) + 180) % 360
approachData['stop'] = haversine('stop',
                                 float(approachData['latitude']),
                                 float(approachData['longitude']),
                                 float(approachData['rwy_heading']),
                                 1.0,
                                 approachData)
approachData['rwy'] = haversine('rwy',
                                float(approachData['latitude']),
                                float(approachData['longitude']),
                                float(approachData['rwy_heading']),
                                0.0,
                                approachData)
approachData['faf'] = haversine('faf',
                                float(approachData['latitude']),
                                float(approachData['longitude']),
                                reverse_heading,
                                float(approachData['faf_distance']),
                                approachData)
approachData['iaf'] = haversine('iaf',
                                float(approachData['latitude']),
                                float(approachData['longitude']),
                                reverse_heading,
                                float(approachData['iaf_distance']),
                                approachData)
approachData['app1'] = haversine('app1',
                                 float(approachData['latitude']),
                                 float(approachData['longitude']),
                                 reverse_heading,
                                 float(approachData['app1_distance']),
                                 approachData)
approachData['app2'] = haversine('app2',
                                 float(approachData['latitude']),
                                 float(approachData['longitude']),
                                 reverse_heading,
                                 float(approachData['app2_distance']),
                                 approachData)
approachData['app3'] = haversine('app3',
                                 float(approachData['latitude']),
                                 float(approachData['longitude']),
                                 reverse_heading,
                                 float(approachData['app3_distance']),
                                 approachData)

if (DEBUG > 0):
    print approachData

    for k in ['app3', 'app2', 'app1', 'iaf', 'faf', 'rwy', 'stop']:
        print "%s ::" % (k)
        print "    Name :: %s" % (approachData[k]['name'])
        print "     Lat :: %.4f" % (float(approachData[k]['lat']))
        print "     Lon :: %.4f" % (float(approachData[k]['lon']))
        print "    Dist :: %.4f km" % (float(approachData[k]['dist']))
        print "     Alt :: %.4f m" % (float(approachData[k]['alt']))
        print ""

    hd = haversineDistance(float(approachData['app3']['lat']),
                           float(approachData['app3']['lon']),
                           float(approachData['stop']['lat']),
                           float(approachData['stop']['lon']),
                           float(approachData['radius']))
    print "Total flight plan length: %.4f km" % (hd / 1000.00)

    logFlightPlan(approachData)

This should produce the desired results.  Again, specify radius in km (6371 for earth, 600 for kerbin).  If you do not specify a radius, it defaults to Kerbin's 600km.

so general usage instructions:

1. put your craft on the runway threshhold.

2. record the altitude on the altimeter (make sure it's MSL, not AGL - if it's 0, it's wrong, and get rid of that mod that changes everything to AGL :) )

3. record the current latitude and longitude

4. record the current heading in degrees

5. convert current lat/lon into signed decimal - ie 17.5 latitude -48.6 longitude.  not the d/m/s NSEW format.  there are numerous online converters.  This works the same for any planet, it is agnostic to planet size

Pass these values as commandline args to the script.

Run the script with -h or --help as the sole argument to see all the possible options.

 

** NOTE - real-world glideslopes are 3 degrees except in some weird cases.  Kramax seems to like 4, at least for ILS 09.  The script defaults to 4 degrees.  Override with -g or --glideslope if you want 3 (or any other slope).

Edited by ss8913
Link to comment
Share on other sites

17 hours ago, Observe said:

@ss8913Works like a charm! I'll be creating more flight paths and will let you know if I encounter any problems or have any suggestions. I recommend this script should be included in the opening post so it doesn't get buried in this thread. Thank you.

glad it's working!  yeah that'd be fine as a pinned post, if the author of the mod wishes to.. and/or it could be included with the mod.  BSD license means you can do pretty much anything as long as you leave my name in the script header somewhere :P ... if you make any good plans for it, specifically for the new remastered kerbinside airports (ie the ones by Admiral Tigerclaw), or the new desert airport.. would love to see some.  I can't play actual ksp for a few more days yet til I'm back with my regular computer :(

Link to comment
Share on other sites

On 10/3/2018 at 4:09 AM, ss8913 said:

alrighty, yes, I was doing the math slightly off and not converting radians back to degrees.. and a few other things... this version works now :)  I tested it against the Kramax default ILS 09 plan and got roughly the same results (slightly different because I wasn't rounding things and just using a 4 degree glideslope; which I suppose is required to clear those mountains from MAXKY :) )


#!/usr/bin/python
#
# fpgen.py
# KSP/Kramax Autopilot flightplan generator
# (C) 2018 Scott Stone <[email protected]>
#
# give this a runway threshhold lat/lon in decimal, plus a runway heading
# it will make an approach flightplan.  Defaults can be overridden on the
# commandline.
#
# haversine formulas courtesy of: https://www.movable-type.co.uk/scripts/latlong.html
# other trig formula courtesy of 11th grade math
#
# THIS VERSION IS FOR PYTHON 2.x
# IT WILL NOT WORK AS-IS IN PYTHON 3.x
# I hear if you put parens around all the print functions, it will.
#

import sys
import math
import argparse
import re

### DEFAULTS

# kerbals use metric, distances are in km
# distances are from the runway threshhold, total not incremental
_FAF_DISTANCE = 13
_IAF_DISTANCE = 23
_APP1_DISTANCE = 50
_APP2_DISTANCE = 100
_APP3_DISTANCE = 200
_FAF_ALTITUDE = -1
_IAF_ALTITUDE = -1
_APP1_ALTITUDE = 6500
_APP2_ALTITUDE = 10000
_APP3_ALTITUDE = 12000
# flare height in meters
_FLARE = 25
# glideslope in degrees
_GS = 4
# planetary radius in km, default is 600 for Kerbin
_PLANET = "Kerbin"
_RADIUS = 600.000
# fix names
fix_names = {
    'stop': 'STOP',
    'rwy': 'FLARE',
    'faf': 'FAF',
    'iaf': 'IAF',
    'app1': 'LOKEY',
    'app2': 'HIKEY',
    'app3': 'MAXKY'
}

# general options
SCRIPT_NAME = "fpgen"
SCRIPT_VERSION = "1.1.0"
SCRIPT_AUTHOR = "Scott Stone <[email protected]>"
SCRIPT_COPYRIGHT = "(C) 2018 Scott Stone - distributed under BSD License"
DEBUG = 0
NEWLINEFORMAT = "unix"


### SUBROUTINES

def dbg(msg):
    if (DEBUG > 0):
        sys.stderr.write("DEBUG: %s\n" % (msg))


def log(msg):
    newline = "\n"
    if (NEWLINEFORMAT != "unix"):
        newline = "\r\n"
    sys.stdout.write("%s%s" % (msg, newline))


# this function does some of the work
# uses the law of sines to find the altitude based on the glideslope angle
# ( sin alpha / a ) == ( sin beta / b )
# or in practical terms:
# ( sin glideslope / altitude) == ( sin topangle / distance )
# sin glideslope == ( altitude * sin topangle ) / distance
# distance * sin glideslope == altitude * sin topangle
# (distance * sin glideslope) / sin topangle == altitude
def calculateAltitude(_dist, slopeAngle):
    # _dist is in km, we need m
    dist = (_dist * 1000)
    topAngle = float(180 - (90 + slopeAngle))
    altitude = ((float(dist) * math.sin(math.radians(slopeAngle))) / (math.sin(math.radians(topAngle))))
    return altitude


# radius given in km here
# lat/lon in signed decimal degrees
# returns a value in km
# TESTED: this function works properly
def haversineDistance(lat1, lon1, lat2, lon2, radius):
    dbg("haversineDistance received [%.4f / %.4f], [%.4f / %.4f], %.4f km radius" % (lat1, lon1, lat2, lon2, radius))
    R = (radius * 1000)
    phi_1 = math.radians(lat1)
    phi_2 = math.radians(lat2)
    delta_phi = math.radians(lat2 - lat1)
    delta_lambda = math.radians(lon2 - lon1)
    a = math.sin(delta_phi / 2.0) ** 2 + math.cos(phi_1) * math.cos(phi_2) * math.sin(delta_lambda / 2.0) ** 2
    c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
    d = R * c
    # get kilometers
    dkm = d / 1000.0
    return dkm


# this function does most of the work
def haversine(key, lat, lon, hdg, dist, appdata):
    # ad = float(float(dist) / float(appdata['radius']))
    radius1 = (float(appdata['radius']) * 1000)
    dist1 = (dist * 1000)
    # ad = math.radians(float(float(dist1) / float ( radius1 ) ) )
    ad = float(float(dist1) / float(radius1))
    lat1 = math.radians(float(lat))
    lon1 = math.radians(float(lon))
    hdg1 = math.radians(float(hdg))
    dbg("haversine function has %.4f/%.4f heading %.4f distance %.4f km" % (
        float(lat), float(lon), float(hdg), float(dist)))
    dbg("converted values %.8f/%.8f heading %.4f radians distance %.4f m, planetary radius %.4fm" % (
        lat1, lon1, hdg1, dist1, radius1))
    lat2 = math.asin((math.sin(lat1) * math.cos(ad)) + math.cos(lat1) * math.sin(ad) * math.cos(hdg1))
    lon2 = lon1 + math.atan2(math.sin(hdg1) * math.sin(ad) * math.cos(lat1),
                             math.cos(ad) - math.sin(lat1) * math.sin(lat2))
    rd = dict()
    rd['lat'] = math.degrees(lat2)
    rd['lon'] = math.degrees(lon2)
    rd['dist'] = dist
    rd['name'] = fix_names[key]
    # get altitudes from law of sines using glideslope for iaf to faf to runway else static
    altKey = "%s_altitude" % (key)
    if (int(appdata[altKey]) != -1):
        rd['alt'] = int(appdata[altKey])
    else:
        rd['alt'] = (calculateAltitude(dist, float(appdata['glideslope'])) +
                     int(appdata['rwy_altitude']))
    return rd


def logFlightPlan(appdata):
    log("KramaxAutoPilotPlans")
    log("{")
    log("    %s" % (appdata['planet']))
    log("    {")
    log("        FlightPlan")
    log("        {")
    log("            planet = %s" % (appdata['planet']))
    log("            name = %s" % (appdata['fpname']))
    log("            description = %s" % (appdata['fpdesc']))
    log("            WayPoints")
    log("            {")
    for k in ['app3', 'app2', 'app1', 'iaf', 'faf', 'rwy', 'stop']:
        log("                WayPoint")
        log("                {")
        log("                    Vertical = true")
        if (k == "iaf"):
            log("                    IAF = true")
        if (k == "faf"):
            log("                    FAF = true")
        if (k == "rwy"):
            log("                    RW = true")
        if (k == "stop"):
            log("                    Stop = true")
        log("                    lat = %.8f" % (appdata[k]['lat']))
        log("                    lon = %.8f" % (appdata[k]['lon']))
        log("                    alt = %d" % (appdata[k]['alt']))
        log("                    name = %s" % (fix_names[k]))
        log("                }")
    log("            }")
    log("        }")
    log("    }")
    log("}")


### MAIN EXECUTION

# dict to store our configuration values
approachData = dict()

# commandline args
parser = argparse.ArgumentParser()
parser.add_argument("-d", "--debug", help="Enable debugging", action="store_true")
parser.add_argument("-p", "--planet", help="Planet Name (dfl: %s)" % (_PLANET), default=_PLANET)
parser.add_argument("-g", "--glideslope", default=_GS, help="Specify glideslope in degrees, dfl %.2f" % (_GS))
parser.add_argument("-n", "--newlineformat", help="dos or unix newlines.  dfl: unix", default="unix")
parser.add_argument("--name", help="Flight Plan Name (required)")
parser.add_argument("--description", help="Flight Plan Description (required)")
parser.add_argument("--latitude", type=float, help="Specify rwy latitude (required)")
parser.add_argument("--longitude", type=float, help="Specify rwy longitude (required)")
parser.add_argument("--heading", type=float, help="Runway heading in degrees (required)")
parser.add_argument("-f", "--flare", type=int, default=_FLARE, help="Flare height in meters (dfl: %d)" % (_FLARE))
parser.add_argument("-r", "--radius", type=float, default=_RADIUS,
                    help="Planetary radius in km (dfl: %.2f)" % (_RADIUS))
parser.add_argument("--faf", default=_FAF_DISTANCE, help="FAF distance in km (dfl: %.2f)" % (_FAF_DISTANCE))
parser.add_argument("--iaf", default=_IAF_DISTANCE, help="IAF distance in km (dfl: %.2f)" % (_IAF_DISTANCE))
parser.add_argument("--app1", default=_APP1_DISTANCE, help="APP1 distance in km (dfl: %.2f)" % (_APP1_DISTANCE))
parser.add_argument("--app2", default=_APP2_DISTANCE, help="APP2 distance in km (dfl: %.2f)" % (_APP2_DISTANCE))
parser.add_argument("--app3", default=_APP3_DISTANCE, help="APP3 distance in km (dfl: %.2f)" % (_APP3_DISTANCE))
parser.add_argument("-a", "--altitude", type=int, help="Runway altitude in meters MSL (required)")
parser.add_argument("--fafalt", default=_FAF_ALTITUDE, help="FAF Altitude in m (dfl: %.2f)" % (_FAF_ALTITUDE))
parser.add_argument("--iafalt", default=_IAF_ALTITUDE, help="IAF Altitude in m (dfl: %.2f)" % (_IAF_ALTITUDE))
parser.add_argument("--app1alt", default=_APP1_ALTITUDE, help="APP1 Altitude in m (dfl: %.2f)" % (_APP1_ALTITUDE))
parser.add_argument("--app2alt", default=_APP2_ALTITUDE, help="APP2 Altitude in m (dfl: %.2f)" % (_APP2_ALTITUDE))
parser.add_argument("--app3alt", default=_APP3_ALTITUDE, help="APP3 Altitude in m (dfl: %.2f)" % (_APP3_ALTITUDE))
parser.add_argument("-D", "--distanceonly", help="Just calculate distance, X/Y,X/Y", action="store_true")
parser.add_argument("--dlat1", type=float, help="Distance calc latitude 1")
parser.add_argument("--dlon1", type=float, help="Distance calc longitude 1")
parser.add_argument("--dlat2", type=float, help="Distance calc latitude 2")
parser.add_argument("--dlon2", type=float, help="Distance calc longitude 2")

args = parser.parse_args()

if (args.debug):
    DEBUG = 1

if (args.distanceonly):
    dbg("distanceonly coords: [%.4f/%.4f %.4f/%.4f]" % (args.dlat1, args.dlon1, args.dlat2, args.dlon2))
    try:
        d = haversineDistance(args.dlat1, args.dlon1, args.dlat2, args.dlon2, float(args.radius))
        print "Distance is %.4f km" % (float(d))
    except:
        ea = sys.exc_info()
        log("ERROR: %s %s" % (ea[0], ea[1]))
        sys.exit(1)
    sys.exit(0)

if (
        (not args.latitude) or
        (not args.name) or
        (not args.description) or
        (not args.longitude) or
        (not args.heading) or
        (not args.altitude)):
    print "Name/Description/Lat/Lon/Heading/Altitude are required arguments.  Run with -h for help."
    sys.exit(1)

NEWLINEFORMAT = args.newlineformat
approachData['planet'] = args.planet
approachData['fpname'] = args.name
approachData['fpdesc'] = args.description
approachData['glideslope'] = args.glideslope or _GS
approachData['latitude'] = args.latitude
approachData['longitude'] = args.longitude
approachData['rwy_heading'] = args.heading
approachData['rwy_altitude'] = args.altitude + args.flare
approachData['stop_altitude'] = args.altitude
approachData['radius'] = args.radius
approachData['faf_distance'] = args.faf
approachData['iaf_distance'] = args.iaf
approachData['app1_distance'] = args.app1
approachData['app2_distance'] = args.app2
approachData['app3_distance'] = args.app3
approachData['faf_altitude'] = args.fafalt
approachData['iaf_altitude'] = args.iafalt
approachData['app1_altitude'] = args.app1alt
approachData['app2_altitude'] = args.app2alt
approachData['app3_altitude'] = args.app3alt

if (DEBUG > 0):
    print approachData

reverse_heading = (float(approachData['rwy_heading']) + 180) % 360
approachData['stop'] = haversine('stop',
                                 float(approachData['latitude']),
                                 float(approachData['longitude']),
                                 float(approachData['rwy_heading']),
                                 1.0,
                                 approachData)
approachData['rwy'] = haversine('rwy',
                                float(approachData['latitude']),
                                float(approachData['longitude']),
                                float(approachData['rwy_heading']),
                                0.0,
                                approachData)
approachData['faf'] = haversine('faf',
                                float(approachData['latitude']),
                                float(approachData['longitude']),
                                reverse_heading,
                                float(approachData['faf_distance']),
                                approachData)
approachData['iaf'] = haversine('iaf',
                                float(approachData['latitude']),
                                float(approachData['longitude']),
                                reverse_heading,
                                float(approachData['iaf_distance']),
                                approachData)
approachData['app1'] = haversine('app1',
                                 float(approachData['latitude']),
                                 float(approachData['longitude']),
                                 reverse_heading,
                                 float(approachData['app1_distance']),
                                 approachData)
approachData['app2'] = haversine('app2',
                                 float(approachData['latitude']),
                                 float(approachData['longitude']),
                                 reverse_heading,
                                 float(approachData['app2_distance']),
                                 approachData)
approachData['app3'] = haversine('app3',
                                 float(approachData['latitude']),
                                 float(approachData['longitude']),
                                 reverse_heading,
                                 float(approachData['app3_distance']),
                                 approachData)

if (DEBUG > 0):
    print approachData

    for k in ['app3', 'app2', 'app1', 'iaf', 'faf', 'rwy', 'stop']:
        print "%s ::" % (k)
        print "    Name :: %s" % (approachData[k]['name'])
        print "     Lat :: %.4f" % (float(approachData[k]['lat']))
        print "     Lon :: %.4f" % (float(approachData[k]['lon']))
        print "    Dist :: %.4f km" % (float(approachData[k]['dist']))
        print "     Alt :: %.4f m" % (float(approachData[k]['alt']))
        print ""

    hd = haversineDistance(float(approachData['app3']['lat']),
                           float(approachData['app3']['lon']),
                           float(approachData['stop']['lat']),
                           float(approachData['stop']['lon']),
                           float(approachData['radius']))
    print "Total flight plan length: %.4f km" % (hd / 1000.00)

    logFlightPlan(approachData)

This should produce the desired results.  Again, specify radius in km (6371 for earth, 600 for kerbin).  If you do not specify a radius, it defaults to Kerbin's 600km.

so general usage instructions:

1. put your craft on the runway threshhold.

2. record the altitude on the altimeter (make sure it's MSL, not AGL - if it's 0, it's wrong, and get rid of that mod that changes everything to AGL :) )

3. record the current latitude and longitude

4. record the current heading in degrees

5. convert current lat/lon into signed decimal - ie 17.5 latitude -48.6 longitude.  not the d/m/s NSEW format.  there are numerous online converters.  This works the same for any planet, it is agnostic to planet size

Pass these values as commandline args to the script.

Run the script with -h or --help as the sole argument to see all the possible options.

 

** NOTE - real-world glideslopes are 3 degrees except in some weird cases.  Kramax seems to like 4, at least for ILS 09.  The script defaults to 4 degrees.  Override with -g or --glideslope if you want 3 (or any other slope).

I last line of script need to remove leading whitespace or it does not give output without -d

 

Link to comment
Share on other sites

This thread is quite old. Please consider starting a new thread rather than reviving this one.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...