#!/bin/sh
# the next line restarts using wish \
exec wish "$0" "$@"

#
# muCalc
#
# Script to compute bearing, reverse bearing, and range from a local
# grid square and a remote latitude and longitude or grid.
#
# Tom Mayo N1MU - 2002-01-04
#

#
#  Valid_Grid - procedure to determine if the variable contains
#               two capital letters followed by two numbers and
#               finally optionally followed by two lower case
#               letters.
#

proc Valid_Grid { x } {
  set y [ string toupper $x ]
  return [regexp {^[A-Z][A-Z][0-9][0-9]([A-Z][A-Z])?$} $y]
}

#
#  To_LatLon - procedure to convert a four- or six-digit grid square
#              into latitude and longitude.
#
#  Arguments - grid - grid square to convert
#              local_remote_n - if 1, result is internal
#                               if 0, result is set for remote station
#

proc To_LatLon { grid local_remote_n } {
  global stuff

  set temp [ string toupper $grid ]

  if { [ Valid_Grid $grid ] } {

    if { [ scan $temp "%c%c%c%c%c%c" lon1 lat1 lon2 lat2 lon3 lat3 ] < 6 } {
      set lon3 77
      set lat3 77
    }
  
    set lat [ expr ( $lat1 - 65.0) * 10.0 ]
    set lon [ expr ( $lon1 - 65.0) * 20.0 ]
  
    set lat [ expr $lat + $lat2 - 48.0 ]
    set lon [ expr $lon + ($lon2 - 48.0) * 2.0 ]
  
    set lat [ expr $lat + 1.25 / 60.0 + \
      ( $lat3 - 65.0 ) / 24.0 ]
    set lon [ expr $lon + 2.5 / 60.0 + \
      ( $lon3 - 65.0 - 12.0 * (int($lon) % 2)) / 12.0 ]
  
    set lat [ expr $lat - 90.0 ]
    set lon [ expr $lon - 180.0 ]
  
    if { $local_remote_n == 0 } {
      set stuff(td_d) $lat
      set stuff(gd_d) $lon
    } else {
      set stuff(ltd_d) $lat
      set stuff(lgd_d) $lon
    }
  } else {
    if { $local_remote_n == 0 } {
      set stuff(td_d) 0
      set stuff(gd_d) 0
    } else {
      set stuff(ltd_d) 0
      set stuff(lgd_d) 0
    }
  }
}

#
# To_grid - function to convert decimal lat/lon to a six-digit grid square
#

proc To_grid { } {
  global stuff

  set mylon [ expr 90 + $stuff(td_d) ]
  set mylat [ expr 180 + $stuff(gd_d) ]

  set grid [ format "%c"    [ expr 65 + int( ( $mylat ) / 20 ) ] ]
  append grid [ format "%c" [ expr 65 + int( ( $mylon ) / 10 ) ] ]

  append grid [ format "%c" [ expr 48 + int( ( $mylat ) / 2 ) % 10 ] ]
  append grid [ format "%c" [ expr 48 + int( $mylon ) % 10 ] ]

  append grid [ format "%c" \
    [ expr 97 + ( int( $mylat ) % 2 ) * 12 + \
      int( ( $mylat - int( $mylat ) ) * 12 ) ] ]
  append grid [ format "%c" \
    [ expr 97 + int( ( $mylon - int( $mylon ) ) * 24 ) ] ]

  set stuff(grid) $grid
}

#
# Do_grid - what to do if the user clicks the Calc button next to
#           the Grid field.
#

proc Do_grid { } {
  global stuff

  To_LatLon $stuff(grid) 0
  Do_d 0
  Bear_Calc
}

#
# Do_dm - what to do if the user clicks the Calc button next to
#         the degrees/minutes fields.
#

proc Do_dm { } {
  global stuff

  set stuff(tdm_d) [ expr int($stuff(tdm_d)) ]
  set stuff(gdm_d) [ expr int($stuff(gdm_d)) ]

  if { $stuff(tdm_d) < 0 } {
    set stuff(td_d) [ expr $stuff(tdm_d) - $stuff(tdm_m) / 60.0 ]
  } else {
    set stuff(td_d) [ expr $stuff(tdm_d) + $stuff(tdm_m) / 60.0 ]
  }

  if { $stuff(gdm_d) < 0 } {
    set stuff(gd_d) [ expr $stuff(gdm_d) - $stuff(gdm_m) / 60.0 ]
  } else {
    set stuff(gd_d) [ expr $stuff(gdm_d) + $stuff(gdm_m) / 60.0 ]
  }
  
  To_grid
  Bear_Calc
}

#
# Do_d - what to do if the user clicks the Calc button next to
#        the degrees fields.
#

proc Do_d { gridflag } {
  global stuff

  set stuff(tdm_d) [ expr int($stuff(td_d)) ]
  set stuff(tdm_m) [ expr abs(($stuff(td_d) - $stuff(tdm_d)) * 60) ]

  set stuff(gdm_d) [ expr int($stuff(gd_d)) ]
  set stuff(gdm_m) [ expr abs(($stuff(gd_d) - $stuff(gdm_d)) * 60) ]
  
  if { $gridflag == 1 } {
    To_grid
  }

  Bear_Calc
}

#
#  Bear_Calc - procedure to compute the bearing and distance between
#              two points given by latitude and longitude.
#

proc Bear_Calc { } {
  global stuff

  set lgrid [ string toupper [ string trim $stuff(lgrid) ] ]
  To_LatLon $lgrid 1
  set stuff(sentll) "$stuff(ltd_d) $stuff(lgd_d)"
  set stuff(recdll) "$stuff(td_d) $stuff(gd_d)"

  if { $stuff(sentll) != $stuff(recdll) } {

    set latlonl $stuff(sentll)
    set latlonr $stuff(recdll)

    set pi [ expr 2 * asin( 1.0 ) ]

    scan $latlonl "%f %f" latl lonl
    scan $latlonr "%f %f" latr lonr

    set dlon [ expr ( $lonl - $lonr ) / 180.0 * $pi ]
    set mylatl [ expr ( $latl / 180.0 * $pi ) ]
    set mylatr [ expr ( $latr / 180.0 * $pi ) ]

    set temp [ expr sin( $mylatl ) * sin( $mylatr ) + \
                    cos( $mylatl ) * cos( $mylatr ) * cos( $dlon ) ]
    set dist [ expr acos( $temp ) ]
    set stuff(rang) [ expr round(10.0 * ($dist * 3960.0)) / 10.0 ]

    set temp [ expr ( sin( $mylatr ) - sin( $mylatl ) * cos( $dist ) ) / \
      ( sin( $dist ) * cos( $mylatl ) ) ]
    set stuff(brng) [ expr round((acos( $temp ) * 180.0 / $pi) * 10.0) / 10.0 ]
    if { $dlon > 0.0 } then {
      set stuff(brng) [ expr 360.0 - $stuff(brng) ]
    }

    set temp [ expr ( sin( $mylatl ) - sin( $mylatr ) * cos( $dist ) ) / \
      ( sin( $dist ) * cos( $mylatr ) ) ]
    set stuff(rbrng) [ expr round((acos( $temp ) * 180.0 / $pi) * 10.0) / 10.0 ]
    if { $dlon < 0.0 } then {
      set stuff(rbrng) [ expr 360.0 - $stuff(rbrng) ]
    }
  } else {
    set stuff(brng) 0.0
    set stuff(rbrng) 0.0
    set stuff(rang) 0.0
  }
}

#
# Set up the labels, entries, and buttons
#

frame .f

frame .f.f0 -borderwidth 2 -relief raised

label .f.f0.l_local   -text "Local Station" -font { helvetica 12 bold }
label .f.f0.l_lgrid   -text "Grid"
entry .f.f0.e_lgrid   -textvariable stuff(lgrid) -width 9

pack .f.f0.l_local .f.f0.l_lgrid .f.f0.e_lgrid -fill y -padx 2 -pady 2

frame .f.f1 -borderwidth 2 -relief raised

button .f.f1.b_dm    -text "Calc" -command Do_dm
button .f.f1.b_d     -text "Calc" -command { Do_d 1 }
button .f.f1.b_grid  -text "Calc" -command Do_grid

label .f.f1.l_remote -text "Remote Station" -font { helvetica 12 bold }
label .f.f1.l_tdm_d  -text "Lat Deg d"
label .f.f1.l_tdm_m  -text "Lat Min m.m"
label .f.f1.l_gdm_d  -text "Lon Deg d"
label .f.f1.l_gdm_m  -text "Lon Min m.m"
label .f.f1.l_td_d   -text "Lat Deg d.d"
label .f.f1.l_gd_d   -text "Lon Deg d.d"
label .f.f1.l_grid   -text "Grid"

entry .f.f1.e_tdm_d  -textvariable stuff(tdm_d) -width 6
entry .f.f1.e_tdm_m  -textvariable stuff(tdm_m) -width 6
entry .f.f1.e_gdm_d  -textvariable stuff(gdm_d) -width 6
entry .f.f1.e_gdm_m  -textvariable stuff(gdm_m) -width 6
entry .f.f1.e_td_d   -textvariable stuff(td_d) -width 6
entry .f.f1.e_gd_d   -textvariable stuff(gd_d) -width 6
entry .f.f1.e_grid   -textvariable stuff(grid) -width 6

grid .f.f1.l_remote - - - - - -
grid .f.f1.l_tdm_d  .f.f1.l_tdm_m  x            .f.f1.l_gdm_d  .f.f1.l_gdm_m  x            x
grid .f.f1.e_tdm_d  .f.f1.e_tdm_m  x            .f.f1.e_gdm_d  .f.f1.e_gdm_m  x            .f.f1.b_dm
grid .f.f1.l_td_d   x            x            .f.f1.l_gd_d   x            x            x
grid .f.f1.e_td_d   x            x            .f.f1.e_gd_d   x            x            .f.f1.b_d
grid x            x            x            .f.f1.l_grid   x            x            x
grid x            x            x            .f.f1.e_grid   x            x            .f.f1.b_grid

grid .f.f1.l_tdm_d -padx 2 -pady 2 -sticky ew
grid .f.f1.l_tdm_m -padx 2 -pady 2 -sticky ew
grid .f.f1.l_gdm_d -padx 2 -pady 2 -sticky ew
grid .f.f1.l_gdm_m -padx 2 -pady 2 -sticky ew
grid .f.f1.e_tdm_d -padx 2 -pady 2 -sticky ew
grid .f.f1.e_tdm_m -padx 2 -pady 2 -sticky ew
grid .f.f1.e_gdm_d -padx 2 -pady 2 -sticky ew
grid .f.f1.e_gdm_m -padx 2 -pady 2 -sticky ew
grid .f.f1.b_dm    -padx 2 -pady 2 -sticky ew

grid .f.f1.l_td_d -padx 2 -pady 2 -sticky ew
grid .f.f1.l_gd_d -padx 2 -pady 2 -sticky ew
grid .f.f1.e_td_d -padx 2 -pady 2 -sticky ew
grid .f.f1.e_gd_d -padx 2 -pady 2 -sticky ew
grid .f.f1.b_d    -padx 2 -pady 2 -sticky ew

grid .f.f1.l_grid -padx 2 -pady 2 -sticky ew
grid .f.f1.e_grid -padx 2 -pady 2 -sticky ew
grid .f.f1.b_grid -padx 2 -pady 2 -sticky ew

frame .f.f2 -borderwidth 2 -relief raised

label .f.f2.l_beardistcalc -text "Bearing and Distance" -font { helvetica 12 bold }
label .f.f2.l_bearing  -text "Bearing d"
label .f.f2.l_rbearing -text "Reverse Bearing d"
label .f.f2.l_distance -text "Distance mi"
entry .f.f2.e_bearing  -textvariable stuff(brng) -width 6
entry .f.f2.e_rbearing -textvariable stuff(rbrng) -width 6
entry .f.f2.e_distance -textvariable stuff(rang) -width 6

grid .f.f2.l_beardistcalc - -
grid .f.f2.l_bearing .f.f2.l_rbearing .f.f2.l_distance
grid .f.f2.e_bearing .f.f2.e_rbearing .f.f2.e_distance

grid .f.f2.l_bearing -padx 2 -pady 2 -sticky ew
grid .f.f2.l_rbearing -padx 2 -pady 2 -sticky ew
grid .f.f2.l_distance -padx 2 -pady 2 -sticky ew
grid .f.f2.e_bearing -padx 2 -pady 2 -sticky ew
grid .f.f2.e_rbearing -padx 2 -pady 2 -sticky ew
grid .f.f2.e_distance -padx 2 -pady 2 -sticky ew

pack .f.f0 .f.f1 .f.f2 -fill both

pack .f

wm title . "muCalc - A Grid, Distance, and Bearing Calculator"
