### Python port of W5GFE's Elevation Plot Perl/Tk Script
### Barry Newberger W5KH
### 23 Nov 03
### Version 1.0

### Python derivative work copyright Barry S. Newberger

#############################################     
#
# copyright July 23, 1999, Bill Walker W5GFE, bw@cs.ecok.edu
# This software is available under the terms of the GNU Public License
#
###########################################

############################################

## the expected RP card is:
##  RP  0  180    1 1001  -90.0  0.0  1.0  0.0  10000.0 0.00E+00

## note:  origins are at top left

###########################################

from Tkinter import *
import sys
import os
import re
import math
import macfs
   

class El_plot:

    def __init__(self, master) :
    
        master.title("Elevation Plots")
  
# set up the big frames

        topframe = Frame(master)
        topframe.pack()
        middleframe = Frame(master)
        middleframe.pack()
        bottomframe = Frame(master)
        bottomframe.pack()
        
        
        self.controlframe = Frame(topframe)
        self.controlframe.pack(side = 'left')
        self.arrlframe = Frame(middleframe)
        self.arrlframe.pack(side = 'left')
        self.polarframe = Frame(middleframe)
        self.polarframe.pack(side = 'left')

        self.cartesianframe = Frame(bottomframe)
        self.cartesianframe.pack(side = 'left')
        self.summaryframe = Frame(bottomframe)
        self.summaryframe.pack(side = 'left')
        
        # Create 'quit' button 
        self.quitter = Button(self.controlframe, text = 'Quit',\
            command = master.destroy)
        self.quitter.pack(side ='left')
           
        self.arrlframelabel = Label(self.arrlframe, text = "ARRL Plot")
        self.arrlframelabel.pack()
        self.polarlabel = Label(self.polarframe, text = "Polar Plot")
        self.polarlabel.pack()
        self.cartesianlabel = Label(self.cartesianframe,\
            text = "Cartesian Plot")
        self.cartesianlabel.pack()
        self.summarylabel =  Label(self.summaryframe, text = "Fact Box")
        self.summarylabel.pack()



########### time to create three canvases -- or maybe four!
   
####### ARRL Graph

        self.arrlgraph = Canvas(self.arrlframe, relief = "sunken",\
            borderwidth = 2, background = "yellow")        
        self.arrlgraphsb_right = Scrollbar(self.arrlframe, orient = "vertical",\
            command = self.arrlgraph.yview)
        self.arrlgraphsb_right.pack(side = 'right', fill = 'y')
        self.arrlgraphsb_bottom = Scrollbar(self.arrlframe, orient = "horizontal",\
            command = self.arrlgraph.xview) 
        self.arrlgraphsb_bottom.pack(side = 'bottom', fill = 'x')
        self.arrlgraph.pack(side = 'left')
        self.arrlgraph.configure(xscrollcommand = self.arrlgraphsb_bottom.set)
        self.arrlgraph.configure(yscrollcommand = self.arrlgraphsb_right.set)
        
####### Cartesian Graph
        
        self.cartesiangraph = Canvas(self.cartesianframe, relief = "sunken",\
        borderwidth = 2, background = "yellow")
        self.cartesiangraphsb_right = Scrollbar(self.cartesianframe, orient = "vertical",\
            command = self.cartesiangraph.yview)
        self.cartesiangraphsb_right.pack(side = 'right', fill = 'y')
        self.cartesiangraphsb_bottom = Scrollbar(self.cartesianframe, orient = "horizontal",\
            command = self.cartesiangraph.xview) 
        self.cartesiangraphsb_bottom.pack(side = 'bottom', fill = 'x')
        self.cartesiangraph.pack(side = 'left')    
        self.cartesiangraph.configure(xscrollcommand = self.cartesiangraphsb_bottom.set)
        self.cartesiangraph.configure(yscrollcommand = self.cartesiangraphsb_right.set)
        
####### Polar Graph

        self.polargraph = Canvas(self.polarframe, relief = "sunken",\
            borderwidth = 2, background = "yellow")
        self.polargraphsb_right = Scrollbar(self.polarframe, orient = "vertical",\
            command = self.polargraph.yview)
        self.polargraphsb_right.pack(side = 'right', fill = 'y')
        self.polargraphsb_bottom = Scrollbar(self.polarframe, orient = "horizontal",\
            command = self.polargraph.xview) 
        self.polargraphsb_bottom.pack(side = 'bottom', fill = 'x')       
        self.polargraph.pack(side = 'left')    
        self.polargraph.configure(xscrollcommand = self.polargraphsb_bottom.set)
        self.polargraph.configure(yscrollcommand = self.polargraphsb_right.set)
        
####### Summaries Canvas

        self.summarygraph = Canvas(self.summaryframe, relief = "sunken",\
            borderwidth = 2, background = "yellow")
        self.summarygraphsb_right = Scrollbar(self.summaryframe, orient = "vertical",\
            command = self.summarygraph.yview)
        self.summarygraphsb_bottom = Scrollbar(self.summaryframe, orient = "horizontal",\
            command = self.summarygraph.xview)
        self.summarygraphsb_right.pack(side = 'right', fill = 'y')
        self.summarygraphsb_bottom.pack(side = 'bottom', fill = 'x')
        self.summarygraph.configure(yscrollcommand = self.summarygraphsb_right.set)
        self.summarygraph.configure(xscrollcommand = self.summarygraphsb_bottom.set)       
        self.summarygraph.pack()

def main():

    gain = []
    newgain = []
    angle = []
    unitx = []
    unity = []
    xarrl = []
    yarrl = []
    i = 0
    line_index = 0
    data_list = []
    

    centerx = 200
    centery = 200
    
###########################################
    root = Tk()
    pattern_graphs = El_plot(root)
    data_list = import_pattern_data()
    maxangle = data_list[0]
    maxgain = data_list[1]
    mingain = data_list[2]
    angle = data_list[3]
    gain = data_list[4]
    line_index = data_list[5] - 1
    maxindex = data_list[6]
    in_file = data_list[7]
############################################

# we need to invent a "unit circle"

    for i in range(0, 181):
        radians = i * 3.14159/180.0
        unitx.insert(i, math.cos(radians))
        unity.insert(i, math.sin(radians))
    fa0 =  0.0     
    scale = maxgain - mingain

# We first worry about creating the ARRL Graph

    i = 0 
    while i <= line_index:
        newgain.insert(i, (gain[i] - mingain))
        radians =  angle[i]* 3.14159/180.0
        xarrl.insert(i, (unitx[i] * math.exp(0.0443255 * newgain[i]))\
            /(math.exp(0.0443255 * scale)))
        yarrl.insert(i, (unity[i] * math.exp(0.0443255 * newgain[i]))\
            /(math.exp(0.0443255 * scale)))
        i = i + 1
    
    arrlsteps = [0.0, 3.0, 6.0, 10.0, 20.0, 30.0, 40.0]
    arrlcounter = 0
    side = 0

    arrlgraphtext_id = pattern_graphs.arrlgraph.create_text(400, 80, text = 'Scales -db')

# draw each circle
    for arrlcounter in (arrlsteps):
        thisstep = (scale  - arrlcounter)
        oldx = 200
        oldy = 200
        j = 0
        while j <= 180 :
            b0 = int ((unity[j] * math.exp(0.0443255 * thisstep))\
                /math.exp(0.0443325 * scale) * 120.0) 
            a0 = int ((unitx[j] * math.exp(0.0443255 * thisstep))\
                /math.exp(0.0443325 * scale) * 120.0)
            arrlgraphline_id = pattern_graphs.arrlgraph.create_line(oldx, oldy, a0 + 200, 200 - b0,\
                width = 2, fill = 'PaleGreen')
            oldx = a0 + 200
            oldy = 200 - b0
            j = j + 1
#label it
        TheLabel = str(arrlcounter)
        arrlgraphtext_id = pattern_graphs.arrlgraph.create_text(400, side * 20 + 100,\
            text = "-" + TheLabel)
        side = side + 1
	
    # do the radials

    j = 0
    while  j <= 180 :
        b0 = int(unity[j] * 120.0)
        a0 = int(unitx[j] * 120.0)
        arrlgraphline_id = pattern_graphs.arrlgraph.create_line(centerx, centery, a0 + 200, 200 - b0,\
            width = 2, fill = 'PaleGreen')
        j = j + 15

    # now plot the data

    oldx = 200
    oldy = centery
    j = 0
    while j<= line_index :
        b0 = int (yarrl[j]   * 120.0)
        a0 = int (xarrl[j]   * 120.0)
        arrlgraphline_id = pattern_graphs.arrlgraph.create_line(oldx, oldy, a0 + 200, 200 - b0,\
            width = 2, fill = 'red')       
        oldx = a0 + 200
        oldy = 200 - b0
        j = j + 1
    
    pattern_graphs.arrlgraph.configure(scrollregion = pattern_graphs.arrlgraph.bbox('all'))

#############################
## Now create the "Linear Polar System
    
    polarsteps = (0.0, 10.0, 20.0, 30.0, 40.0)

    polarcounter = 0
    side = 0
    pattern_graphs.polargraph.create_text(400, 80, text ='Scales -db')


# draw each circle
    
    for polarcounter in (polarsteps) :
        thisstep = (scale  - polarcounter)
        oldx = 200
        oldy = 200
        i = 0
        while i <= 180 : 
            b0 = int(thisstep * unity[i]/scale * 120.0)
            a0 = int (thisstep * unitx[i]/scale * 120.0)
            pattern_graphs.polargraph.create_line(oldx, oldy, a0 + 200, 200 - b0,\
                width = 2, fill = 'PaleGreen')
            oldx = a0 + 200
            oldy = 200 - b0
            i = i + 1
      #label it
        TheLabel = str(polarcounter)
        pattern_graphs.polargraph.create_text(400, side * 20 + 100, text = "-" + TheLabel)
        side = side + 1

    # do the radials

    i = 0
    while i <= 180 : 
        b0 =  int(unity[i] * 120.0)
        a0 = int(unitx[i] * 120.0)
        pattern_graphs.polargraph.create_line(centerx, centery, a0 + 200, 200 - b0,\
            width = 2, fill = 'PaleGreen')
        i = i + 15

    # now plot the data

    oldx =  200
    oldy = centery
    i = 0
    while i <= line_index :
        a0 = int (newgain[i]/scale * unitx[i]   * 120.0)
        b0 = int (newgain[i]/scale * unity[i]   * 120.0)
        pattern_graphs.polargraph.create_line(oldx, oldy, a0 + 200, 200 - b0,\
            width = 2, fill = 'red')
        oldx = a0 + 200
        oldy = 200 - b0
        i = i + 1

    pattern_graphs.polargraph.configure(scrollregion = pattern_graphs.polargraph.bbox('all'))


##################### Cartesian System
############# do the fact box

    factxoffset = 100
    factstep = 15
    facty = 20

    facty = addfact("File is " + in_file, factxoffset, facty, factstep, pattern_graphs)
    
#####################################

    # add tick marks
    
    offsetx = 200
    fa0 =  0.0
    i = 0
    while i <= line_index :
        a0 =  int((i/180.0) * 300.0)
      # vertical line
        pattern_graphs.cartesiangraph.create_line(a0 + offsetx, 0, a0 + offsetx,\
            210, fill = 'PaleGreen')
        pattern_graphs.cartesiangraph.create_line(a0 + offsetx, 0, a0 + offsetx,\
            210, fill = 'PaleGreen')
        pattern_graphs.cartesiangraph.create_line(a0 + offsetx, 225, a0 + offsetx,\
            215, fill = 'PaleGreen')
        PlotThisAngle = int(90 - angle[i])
        pattern_graphs.cartesiangraph.create_text(a0 + offsetx - 5, 235,\
            text = PlotThisAngle)
        i = i + 15

    # and horizontal scales
    
    lowerscale = int(mingain - 1.0)
    if (lowerscale < -60) :
        lowerscale = -60
        facty = addfact("minimum gain adjusted to -60 DB", factxoffset, facty,\
            factstep, pattern_graphs)
#       plotgain = mingain
    plotgain = lowerscale
    while (plotgain <= maxgain) :
        b0 = 200 - int ((plotgain - mingain)/(maxgain - mingain)  * 200.0)
        pattern_graphs.cartesiangraph.create_line(offsetx, b0, offsetx + 300,\
            b0, fill = 'PaleGreen')
        pattern_graphs.cartesiangraph.create_text(offsetx + 350, b0, text = plotgain)
        plotgain =  (plotgain + 6.0)

    # We need a vertical scale and horizontal scale
    
    pattern_graphs.cartesiangraph.create_line(offsetx, 220, offsetx + 300,\
        220, fill = 'PaleGreen')


    # put the data on the graph
    
    fa0 =  offsetx
    oldx = offsetx
    oldy = 200 - int ((gain[0] - mingain)/(maxgain - mingain) * 200.0)
    i = 0
    while i <= line_index :
        b0 = 200 - int((gain[i] - mingain)/(maxgain - mingain)  * 200.0)
        fa0 =  fa0 +  (600.0/360.0)
        a0 = int(fa0)
        pattern_graphs.cartesiangraph.create_line(oldx, oldy, a0, b0, fill = 'red')
        oldx = a0
        oldy = b0
        i = i + 1

    pattern_graphs.cartesiangraph.configure(scrollregion = pattern_graphs.cartesiangraph.bbox('all'))

############# do the fact box
      # and add the "facts"
     
    facty = addfact("Maximum gain is " + str(maxgain) + " DB", factxoffset, facty,\
        factstep, pattern_graphs)
    facty = addfact("Which occurs at elevation of " + str(maxangle), factxoffset, facty,\
        factstep, pattern_graphs)
    facty = addfact("and perhaps at other angles also", factxoffset, facty,\
        factstep, pattern_graphs)
    facty = addfact("Remember 0 degrees is vertical", factxoffset, facty,\
        factstep, pattern_graphs)

      # look for -3db beamwidth by starting at maxindex, and
      # lookup up and down for first values that are less than maxgain - 3.0

      # look for -3db beamwidth by starting at maxindex, and
      # lookup up and down for first values that are less than maxgain - 3.0

    up = maxindex + 1
    while ((gain[up] > maxgain - 3.0) and  (up != maxindex) ) :
	    up = (up + 1) % (line_index + 1)

    down = (maxindex + line_index -1) % (line_index + 1)
    while ((gain[down] > maxgain - 3.0) and (down != maxindex)) :
	    down = (down + line_index - 1) % (line_index + 1)
    threedb =   abs(angle[up] - angle[down])
    facty = addfact("-3 DB beamwidth is " + str(threedb), factxoffset, facty,\
        factstep, pattern_graphs)

    pattern_graphs.summarygraph.configure(scrollregion = pattern_graphs.summarygraph.bbox('all'))

#   Interact...

    root.mainloop()
    
# -- end  main -- #

def import_pattern_data ():

    fsspec, ok = macfs.PromptGetFile('NEC Pattern Output File')
    if not ok:
        sys.exit(0)
    pathname = fsspec.as_pathname()
    in_file = open(pathname, 'r')
    filetuple = fsspec.as_tuple()
    filename = filetuple[2]

    pattern_gain = []
    pattern_angle = []
    line_counter = 0
    max_gain =  -100.0
    min_gain =  500.0
    max_angle =  0.0
    max_index = 0
    my_yes = 0
    my_no = 1
    inpattern = my_no
    done = 0

    patt1obj = re.compile("[\s,]+")
    patt2obj = re.compile("^[\s:]*")

    while (not done) :
        line = in_file.readline()
        if ((line == '') and (inpattern == my_no)):
            done = 1
            print "NOT A NEC OUTPUT FILE OR NO PATTERN"
        line.rstrip()
  # a line consists of several fields ---
  # the first is elevation
  # the second is azimuth
  # the third and fourth are components
  # the fifth is gain
  # the rest are junk
        m = re.search('RADIATION PATTERN', line)
        if m != None :
            inpattern = my_yes
    # skip 7
            line = in_file.readline()
            line = in_file.readline()
            line = in_file.readline()
            line = in_file.readline()
            line = in_file.readline()
            line = in_file.readline()
            line = in_file.readline()
            line = in_file.readline()
            line.rstrip()
    # now we are in the PATTERN until we find a blank line
        if ((len(line) <= 1) and (inpattern == my_yes)):
            inpattern = my_no
            done = 1
        if (inpattern == my_yes):
            line = patt1obj.sub(":", line)
            line = patt2obj.sub("", line)
            TheTokens = re.split(':', line)
            e0 = float(TheTokens[0])
            a0 = float(TheTokens[1])
            g0 = float(TheTokens[4])          
    
# if the gain is very small, just set it to about -60
            if (g0 <=  -60.0):
                g0 =  -60.0
            pattern_gain.insert(line_counter, g0)
            if (g0 > max_gain):
                max_angle = e0
                max_gain = g0
                max_index = line_counter
            if (g0 < min_gain):
                min_gain = g0
#	        pattern_angle[line_counter] = (float) ( 180.0 - line_counter)
            pattern_angle.insert(line_counter, 180 - line_counter)
            line_counter = line_counter + 1
                    
# -- End of while loop--

    return max_angle, max_gain, min_gain, pattern_angle,\
        pattern_gain, line_counter, max_index, filename
        
    in_file.close()
    
# -- end of subroutine import_pattern_data -- #


def addfact (fact_str, factxoffset, fact_y, fact_step, graph_ref ) :
    id = graph_ref.summarygraph.create_text(factxoffset, fact_y, text = fact_str)
    fact_y = fact_y + fact_step
    return fact_y
    
# -- end of subroutine addfact -- #

if __name__ == '__main__': 
    main()
