/*  -------------------------------------------------------------------------
    Loccalc V1.1, written by Marco, IK2PIH, on 24 Aug 02 
    and revised on 22 Nov 20. 
    
    Loccalc calculates longitude and latitude from a 4 or 6 characters world
    wide locator, calculates distance (kilometers and miles) between two
    locators and calculates a locator from longitude and latitude.
    You will be prompted for all inputs and all inputs are validity checked.
    The calculation of beam heading and distance assumes that Earth is a sphere.

    Loccalc is a clone of Locator Calculator V2.0, a free software written in 
    Basic by SM0CCM in 1987, and released by the author as a public domain
    program with no restrictions.

    This software should compile with any C-compiler and run under any OS.
    For better results set EAR to earth radius of your location
    (for my location, north Italy, is 6378 km); my radius should be
    suitable for all locations at the same latitude of my QTH (45 deg N or S).

    --------------------------------------------------------------------------
    Not Copyrighted 2002, 2020, Marco, IK2PIH, bermarco72@gmail.com

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
    ----------------------------------------------------------------------- */



#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>

#ifndef PI
# define PI 3.141592654
#endif

#define ISLON 0
#define ISLAT 1

#define EAR 6378.0   /* Earth radius in north Italy (km) */



int chkloc(char *loc);
void loc2lonlat(char *loc, double *lon, double *lat);
void coord2headist(double lon1, double lat1, double lon2, double lat2, double *cb, double *di, double *dmi);
void uppercase(char *st);
void lonlat2loc(char *loc, double lon, double lat);
int str2deg(char *inputstr, double *res, char t);




int main()
{
char myloc[7];
char otherloc[7];
char inputstr[9];
double mylon,mylat,otherlon,otherlat,cb,di,dmi;
char ch;
int rv;


do
 {printf("--------------------------------------------------------------------------\n");
  printf("  * * *   IK2PIH LOCCALC - a clone of SM0CCM LOCATOR CALCULATOR  * * *\n");
  printf("                                 MAIN MENU\n");
  printf("--------------------------------------------------------------------------\n");
  printf("1. BEAM HEADING AND DISTANCE.\n");
  printf("2. CONVERT LONG/LAT TO LOCATOR.\n");
  printf("3. END\n");

  ch=0;

 while((ch!='1') && (ch!='2') && (ch!='3'))
      {printf("\nMAKE YOUR CHOICE > ");
       scanf("%s",inputstr);
       ch=inputstr[0];}




 /* BEAM HEADING AND DISTANCE */

  if(ch=='1')
   {
     printf("--------------------------------------------------------------------------\n");
     printf("  * * *   IK2PIH LOCCALC - a clone of SM0CCM LOCATOR CALCULATOR  * * *\n");
     printf("                        BEAM HEADING AND DISTANCE\n");
     printf("--------------------------------------------------------------------------\n");

     do {
        printf("---------------INSERT YOUR LOCATOR------------------\n");
        printf("(TO QUIT: ENTER \"END\" INSTEAD OF YOUR LOCATOR)\n");
        printf("YOUR LOCATOR? ");
        scanf("%s",myloc);
        uppercase(myloc);
        rv=chkloc(myloc);


        if(rv==0)
            {printf("WRONG LOCATOR !\n\n");}
        }
     while(rv==0);


     if(rv==-1)
      {loc2lonlat(myloc, &mylon, &mylat);
       printf("\nYOUR LOCATOR IS   = %s\n",myloc);
       printf("YOUR LONGITUDE IS = %.2lf DEG\n",mylon);
       printf("YOUR LATITUDE IS  = %.2lf DEG\n\n\n",mylat);



       do
       {

        do {
	   printf("\n-------------------INSERT A LOCATOR------------------\n");
	   printf("(TO QUIT: ENTER \"END\" INSTEAD OF A LOCATOR)\nLOCATOR? ");
           scanf("%s",otherloc);
           uppercase(otherloc);            /* Read locator and convert in upper case */
           rv=chkloc(otherloc);


           if(rv==0)
               {printf("WRONG LOCATOR !\n\n");}
           }
        while(rv==0);                          /* Repeat if input is not valid */



        if(rv==-1)
           {loc2lonlat(otherloc, &otherlon, &otherlat);
            coord2headist(mylon, mylat, otherlon, otherlat, &cb, &di, &dmi);

            printf("\n************* CALCULATIONS FOR %s *************\n",otherloc);
            printf("POSITION OF %s\n",otherloc);
            printf("LONGITUDE = %.2lf DEG\n",otherlon);
            printf("LATITUDE  = %.2lf DEG\n",otherlat);
	    printf("\nBEAM HEADING AND DISTANCE BETWEEN %s",myloc);
	    printf(" AND %s\n",otherloc);
            printf("BEAM HEADING = %.1lf DEG\n",cb);
            printf("DISTANCE  = %.1lf km\n",di);
            printf("DISTANCE  = %.1lf MLS\n",dmi);
            printf("***************************************************\n\n");
           }

       }
       while(rv!=-2);    /* Repeat if locator != END */

     }

   }






 /* CONVERT LONG/LAT TO LOCATOR */

  if(ch=='2')
   {
     printf("--------------------------------------------------------------------------\n");
     printf("  * * *   IK2PIH LOCCALC - a clone of SM0CCM LOCATOR CALCULATOR  * * *\n");
     printf("                  CONVERT LONGITUDE/LATITUDE TO LOCATOR\n");
     printf("--------------------------------------------------------------------------");
     printf("\n\n");
     printf("INPUT FORMAT FOR LONGITUDE / LATITUDE = Cnnnsdd\n");
     printf("Where - C   = W/E/N/S  and  nnn = degrees.\n");
     printf("        sdd = .dd for decimal degrees.\n");
     printf("        sdd = 'dd for minutes.\n");

     do
       {printf("\nENTER LONGITUDE - C = W for West, E for East.\n");
        printf("> ");
        scanf("%s",inputstr);
        uppercase(inputstr);
        rv=str2deg(inputstr, &mylon, ISLON);
        if(rv==0)
            {printf("WRONG LONGITUDE !\n\n");}
       }
     while(rv==0);


     do
       {printf("\nENTER LATITUDE - C = N for North, S for South.\n");
        printf("> ");
        scanf("%s",inputstr);
        uppercase(inputstr);
        rv=str2deg(inputstr, &mylat, ISLAT);

        if(rv==0)
            {printf("WRONG LATITUDE !\n\n");}
       }
      while(rv==0);


     lonlat2loc(myloc, mylon, mylat);
     printf("\nYOUR LOCATOR IS   = %s\n",myloc);
     printf("YOUR LONGITUDE IS = %.2lf DEG\n",mylon);
     printf("YOUR LATITUDE IS  = %.2lf DEG\n\n",mylat);
   }

 }
while(ch!='3');

return(0);
}









int chkloc(char *loc)
{
/* -------------------------------------------------
      Check valid locator
      Input : char *locator = 4 or 6 characters wwlocator.
      returned value ==  -1 No error. (Valid locator).
      returned value ==  0 Error.   (Invalid locator).
      Note: also string "END" is considered a valid locator, 
            but returned value is -2.
   ------------------------------------------------- */
int lenloc,x,i;
char t;

lenloc=strlen(loc);

if (lenloc==4)
        {strcat(loc,"LM");
         lenloc=6;}                     /* Adjust to middle of square */

x=-1;                                   /* Assumes locator is right */

if (strcmp(loc,"END")==0) x=-2;         /* Return -2 if locator=END */
else if (lenloc!=6) x=0;                /* force to 0 if locator is wrong */
else for(i=0; i<=5; i++)
   {t=loc[i];
    if(((t>'R')||(t<'A'))&&((i==0)||(i==1))) x=0; /*first 2 char from A to R*/
    if(((t>'9')||(t<'0'))&&((i==2)||(i==3))) x=0; /*check the numbers*/
    if(((t>'X')||(t<'A'))&&((i==4)||(i==5))) x=0; /*last 2 char from A to X*/
   }

return(x);
}







void loc2lonlat(char *loc, double *lon, double *lat)
{
/* -------------------------------------------------
        calculate longitude and latitude
        Input : locator   = 6 characters wwlocator.
        Output: lon = longitude in decimal degrees;
                lat  = latitude in decimal degrees;
------------------------------------------------- */

*lon=-180.0+((double)loc[0]-65.0)*20.0+((double)loc[2]-48.0)*2.0+((double)loc[4]-64.5)/12.0;
*lon=-(*lon);
*lat=-90.0+((double)loc[1]-65.0)*10.0+((double)loc[3]-48.0)+((double)loc[5]-64.5)/24.0;
return;
}







void coord2headist(double lon1, double lat1, double lon2, double lat2, double *cb, double *di, double *dmi)
{
/* ------------------------------------
        Calculate beam heading and distance
   Input : lon1  = Longitude 1 in deg.
         : lat1  = Latitude 1 in deg.
         : lon2  = Longitude 1 in deg.
         : lat2  = Latitude 2 in deg.
   Output: cb   = Beam heading in degr.
           di   = Distance in kilometers.
           dmi  = Distance in miles.
 ------------------------------------ */

double lo1,la1,lo2,la2,x;

lo1=lon1*PI/180.0;
la1=lat1*PI/180.0;
lo2=lon2*PI/180.0;
la2=lat2*PI/180.0;

(*di)=acos(cos(la1)*cos(lo1)*cos(la2)*cos(lo2)+cos(la1)*sin(lo1)*cos(la2)*sin(lo2)+sin(la1)*sin(la2))*EAR;
(*dmi)=(*di)/1.609;

x=atan2(sin(lo1-lo2)*cos(la2),cos(la1)*sin(la2)-sin(la1)*cos(la2)*cos(lo1-lo2))/PI*180.0;

if(x<0)
    (*cb)=x+360.0;
else
    (*cb)=x;

return;
}









void uppercase(char *st)
{
int le,i;

/* -------------------------------------------------
       CONVERT STRING TO UPPER CASE
       Input : st = Variable length. Upper and/or lower case.
       Output: st = Variable length. Upper case only.
   ------------------------------------------------- */

le=strlen(st);

if(le!=0)
 {
  for(i=0;i<le;i++)
  {
   if((st[i]>='a')&&(st[i]<='z'))
	 st[i]=st[i]-32;
  }
 }
return;
}






void lonlat2loc(char *loc, double lon, double lat)
{
double lo,la;
int alo,bla,clo,dla,elo,fla;

/* -------------------------------------------------
   Calculate locator from longitude and latitude
   Input : lon = Longitude in decimal degrees (+ = West;  - = East).
           lat = Latitude in decimal degrees (+ = North; - = South).
   Output: locator = 6 characters world wide locator.
   ------------------------------------------------- */

lo=(-lon+180.0)/20.0;
la=(lat+90.0)/10.0;

alo=(int)floor(lo);
bla=(int)floor(la);

lo=(lo-(double)alo)*10.0;
la=(la-(double)bla)*10.0;

clo=(int)floor(lo);
dla=(int)floor(la);

elo=(int)floor((lo-(double)clo)*24.0);
fla=(int)floor((la-(double)dla)*24.0);

loc[0]=(char)(alo+'A');
loc[1]=(char)(bla+'A');
loc[2]=(char)(clo+'0');
loc[3]=(char)(dla+'0');
loc[4]=(char)(elo+'A');
loc[5]=(char)(fla+'A');
loc[6]='\0';

return;
}








int str2deg(char *inputstr, double *res, char t)
{
/* ------------------------------------------------
      Get decimal degrees from input string
      Return 0 if error occurs; Return -1 if OK;
   ------------------------------------------------- */
char degs[4];
char fractdeg[3];
char c,d;
double dg,dy;
double decdg;
double mins;
int i;



c=inputstr[0];

if((t==ISLON && c!='W' && c!='E') || (t==ISLAT && c!='N' && c!='S'))
                                                                return(0);

/* Extract degrees from string and convert to double*/
for(i=1;i<=3;i++)
    {if(inputstr[i]>='0' && inputstr[i]<='9') degs[i-1]=inputstr[i];
     else return(0);}
degs[3]='\0';

sscanf(degs,"%lf",&dg);

/* Extract fract of deg from string and converts minutes to decimal deg*/
for(i=5;i<=6;i++)
    {if(inputstr[i]>='0' && inputstr[i]<='9') fractdeg[i-5]=inputstr[i];
     else return(0);}
fractdeg[2]='\0';

d=inputstr[4];

if(d=='.')
      sscanf(fractdeg,"%lf",&decdg);

else if((d=='\'') && (fractdeg[0]<='5'))
     {sscanf(fractdeg,"%lf",&mins);
      decdg=mins/60.0*100.0;}

else return(0);


/* Build number in decimal degrees and checks result */
dg=dg+(decdg/100.0);
if((t==ISLON && dg>180.0) || (t==ISLAT && dg>90.0))
                                                return(0);

/* Minus if longitude east or latitude south */
if(c=='E' || c=='S') {dy=0-dg;
                      dg=dy;}

/* Export result */
*res=dg;

return(-1);
}
