direwolf/geotranz/mgrs.c

1348 lines
46 KiB
C
Raw Permalink Normal View History

Version 1.2 new file: A-Better-APRS-Packet-Demodulator-Part-1-1200-baud.pdf new file: A-Better-APRS-Packet-Demodulator-Part-2-9600-baud.pdf modified: APRStt-Implementation-Notes.pdf modified: CHANGES.txt new file: Makefile modified: Makefile.linux modified: Makefile.win deleted: Quick-Start-Guide-Windows.pdf modified: Raspberry-Pi-APRS-Tracker.pdf modified: Raspberry-Pi-APRS.pdf modified: User-Guide.pdf modified: aclients.c modified: aprs_tt.c modified: aprs_tt.h modified: atest.c modified: audio.c modified: audio.h modified: audio_win.c modified: ax25_pad.c modified: ax25_pad.h modified: beacon.c modified: beacon.h modified: config.c modified: config.h modified: decode_aprs.c modified: decode_aprs.h modified: demod.c modified: demod.h modified: demod_9600.c modified: demod_9600.h modified: demod_afsk.c modified: digipeater.c modified: digipeater.h modified: direwolf.c modified: direwolf.conf deleted: direwolf.desktop modified: direwolf.h new file: direwolf.spec new file: direwolf.txt new file: dlq.c new file: dlq.h modified: dsp.c new file: dtime_now.c new file: dtime_now.h modified: dtmf.c modified: dtmf.h new file: dwespeak.bat new file: dwespeak.sh modified: fsk_demod_state.h modified: gen_packets.c modified: gen_tone.c new file: geotranz/README-FIRST.txt new file: geotranz/error_string.c new file: geotranz/error_string.h new file: geotranz/mgrs.c new file: geotranz/mgrs.h new file: geotranz/polarst.c new file: geotranz/polarst.h new file: geotranz/readme.txt new file: geotranz/releasenotes.txt new file: geotranz/tranmerc.c new file: geotranz/tranmerc.h new file: geotranz/ups.c new file: geotranz/ups.h new file: geotranz/usng.c new file: geotranz/usng.h new file: geotranz/utm.c new file: geotranz/utm.h modified: hdlc_rec.c modified: hdlc_rec.h modified: hdlc_rec2.c modified: hdlc_rec2.h modified: hdlc_send.c modified: igate.c modified: igate.h modified: kiss_frame.c modified: kiss_frame.h modified: kissnet.c modified: latlong.c modified: latlong.h modified: ll2utm.c modified: log.c modified: log.h new file: man1/aclients.1 new file: man1/atest.1 new file: man1/decode_aprs.1 new file: man1/direwolf.1 new file: man1/gen_packets.1 new file: man1/ll2utm.1 new file: man1/log2gpx.1 new file: man1/text2tt.1 new file: man1/tt2text.1 new file: man1/utm2ll.1 modified: multi_modem.c modified: multi_modem.h new file: pfilter.c new file: pfilter.h modified: ptt.c modified: ptt.h deleted: pttest.c modified: rdq.c new file: recv.c new file: recv.h modified: redecode.c modified: redecode.h modified: rrbb.c modified: rrbb.h modified: server.c modified: server.h modified: telemetry.c modified: telemetry.h modified: textcolor.c modified: textcolor.h modified: tocalls.txt modified: tq.c modified: tq.h modified: tt_text.c modified: tt_text.h modified: tt_user.c modified: tt_user.h new file: ttcalc.c deleted: udp_test.c deleted: utm/LatLong-UTMconversion.c deleted: utm/LatLong-UTMconversion.h deleted: utm/README.txt deleted: utm/SwissGrid.cpp deleted: utm/UTMConversions.cpp deleted: utm/constants.h modified: utm2ll.c modified: version.h new file: xid.c modified: xmit.c modified: xmit.h
2015-07-27 01:17:23 +00:00
/***************************************************************************/
/* RSC IDENTIFIER: MGRS
*
* ABSTRACT
*
* This component converts between geodetic coordinates (latitude and
* longitude) and Military Grid Reference System (MGRS) coordinates.
*
* ERROR HANDLING
*
* This component checks parameters for valid values. If an invalid value
* is found, the error code is combined with the current error code using
* the bitwise or. This combining allows multiple error codes to be
* returned. The possible error codes are:
*
* MGRS_NO_ERROR : No errors occurred in function
* MGRS_LAT_ERROR : Latitude outside of valid range
* (-90 to 90 degrees)
* MGRS_LON_ERROR : Longitude outside of valid range
* (-180 to 360 degrees)
* MGRS_STR_ERROR : An MGRS string error: string too long,
* too short, or badly formed
* MGRS_PRECISION_ERROR : The precision must be between 0 and 5
* inclusive.
* MGRS_A_ERROR : Semi-major axis less than or equal to zero
* MGRS_INV_F_ERROR : Inverse flattening outside of valid range
* (250 to 350)
* MGRS_EASTING_ERROR : Easting outside of valid range
* (100,000 to 900,000 meters for UTM)
* (0 to 4,000,000 meters for UPS)
* MGRS_NORTHING_ERROR : Northing outside of valid range
* (0 to 10,000,000 meters for UTM)
* (0 to 4,000,000 meters for UPS)
* MGRS_ZONE_ERROR : Zone outside of valid range (1 to 60)
* MGRS_HEMISPHERE_ERROR : Invalid hemisphere ('N' or 'S')
*
* REUSE NOTES
*
* MGRS is intended for reuse by any application that does conversions
* between geodetic coordinates and MGRS coordinates.
*
* REFERENCES
*
* Further information on MGRS can be found in the Reuse Manual.
*
* MGRS originated from : U.S. Army Topographic Engineering Center
* Geospatial Information Division
* 7701 Telegraph Road
* Alexandria, VA 22310-3864
*
* LICENSES
*
* None apply to this component.
*
* RESTRICTIONS
*
*
* ENVIRONMENT
*
* MGRS was tested and certified in the following environments:
*
* 1. Solaris 2.5 with GCC version 2.8.1
* 2. Windows 95 with MS Visual C++ version 6
*
* MODIFICATIONS
*
* Date Description
* ---- -----------
* 16-11-94 Original Code
* 15-09-99 Reengineered upper layers
* 02-05-03 Corrected latitude band bug in GRID_UTM
* 08-20-03 Reengineered lower layers
*/
/***************************************************************************/
/*
* INCLUDES
*/
#include <ctype.h>
#include <math.h>
#include <stdio.h>
#include <string.h>
#include "ups.h"
#include "utm.h"
#include "mgrs.h"
/*
* ctype.h - Standard C character handling library
* math.h - Standard C math library
* stdio.h - Standard C input/output library
* string.h - Standard C string handling library
* ups.h - Universal Polar Stereographic (UPS) projection
* utm.h - Universal Transverse Mercator (UTM) projection
* mgrs.h - function prototype error checking
*/
/***************************************************************************/
/*
* GLOBAL DECLARATIONS
*/
#define DEG_TO_RAD 0.017453292519943295 /* PI/180 */
#define RAD_TO_DEG 57.29577951308232087 /* 180/PI */
#define LETTER_A 0 /* ARRAY INDEX FOR LETTER A */
#define LETTER_B 1 /* ARRAY INDEX FOR LETTER B */
#define LETTER_C 2 /* ARRAY INDEX FOR LETTER C */
#define LETTER_D 3 /* ARRAY INDEX FOR LETTER D */
#define LETTER_E 4 /* ARRAY INDEX FOR LETTER E */
#define LETTER_F 5 /* ARRAY INDEX FOR LETTER F */
#define LETTER_G 6 /* ARRAY INDEX FOR LETTER G */
#define LETTER_H 7 /* ARRAY INDEX FOR LETTER H */
#define LETTER_I 8 /* ARRAY INDEX FOR LETTER I */
#define LETTER_J 9 /* ARRAY INDEX FOR LETTER J */
#define LETTER_K 10 /* ARRAY INDEX FOR LETTER K */
#define LETTER_L 11 /* ARRAY INDEX FOR LETTER L */
#define LETTER_M 12 /* ARRAY INDEX FOR LETTER M */
#define LETTER_N 13 /* ARRAY INDEX FOR LETTER N */
#define LETTER_O 14 /* ARRAY INDEX FOR LETTER O */
#define LETTER_P 15 /* ARRAY INDEX FOR LETTER P */
#define LETTER_Q 16 /* ARRAY INDEX FOR LETTER Q */
#define LETTER_R 17 /* ARRAY INDEX FOR LETTER R */
#define LETTER_S 18 /* ARRAY INDEX FOR LETTER S */
#define LETTER_T 19 /* ARRAY INDEX FOR LETTER T */
#define LETTER_U 20 /* ARRAY INDEX FOR LETTER U */
#define LETTER_V 21 /* ARRAY INDEX FOR LETTER V */
#define LETTER_W 22 /* ARRAY INDEX FOR LETTER W */
#define LETTER_X 23 /* ARRAY INDEX FOR LETTER X */
#define LETTER_Y 24 /* ARRAY INDEX FOR LETTER Y */
#define LETTER_Z 25 /* ARRAY INDEX FOR LETTER Z */
#define MGRS_LETTERS 3 /* NUMBER OF LETTERS IN MGRS */
#define ONEHT 100000.e0 /* ONE HUNDRED THOUSAND */
#define TWOMIL 2000000.e0 /* TWO MILLION */
#define TRUE 1 /* CONSTANT VALUE FOR TRUE VALUE */
#define FALSE 0 /* CONSTANT VALUE FOR FALSE VALUE */
#define PI 3.14159265358979323e0 /* PI */
#define PI_OVER_2 (PI / 2.0e0)
#define MIN_EASTING 100000
#define MAX_EASTING 900000
#define MIN_NORTHING 0
#define MAX_NORTHING 10000000
#define MAX_PRECISION 5 /* Maximum precision of easting & northing */
#define MIN_UTM_LAT ( (-80 * PI) / 180.0 ) /* -80 degrees in radians */
#define MAX_UTM_LAT ( (84 * PI) / 180.0 ) /* 84 degrees in radians */
#define MIN_EAST_NORTH 0
#define MAX_EAST_NORTH 4000000
/* Ellipsoid parameters, default to WGS 84 */
double MGRS_a = 6378137.0; /* Semi-major axis of ellipsoid in meters */
double MGRS_f = 1 / 298.257223563; /* Flattening of ellipsoid */
char MGRS_Ellipsoid_Code[3] = {'W','E',0};
/*
* CLARKE_1866 : Ellipsoid code for CLARKE_1866
* CLARKE_1880 : Ellipsoid code for CLARKE_1880
* BESSEL_1841 : Ellipsoid code for BESSEL_1841
* BESSEL_1841_NAMIBIA : Ellipsoid code for BESSEL 1841 (NAMIBIA)
*/
const char* CLARKE_1866 = "CC";
const char* CLARKE_1880 = "CD";
const char* BESSEL_1841 = "BR";
const char* BESSEL_1841_NAMIBIA = "BN";
typedef struct Latitude_Band_Value
{
long letter; /* letter representing latitude band */
double min_northing; /* minimum northing for latitude band */
double north; /* upper latitude for latitude band */
double south; /* lower latitude for latitude band */
double northing_offset; /* latitude band northing offset */
} Latitude_Band;
static const Latitude_Band Latitude_Band_Table[20] =
{{LETTER_C, 1100000.0, -72.0, -80.5, 0.0},
{LETTER_D, 2000000.0, -64.0, -72.0, 2000000.0},
{LETTER_E, 2800000.0, -56.0, -64.0, 2000000.0},
{LETTER_F, 3700000.0, -48.0, -56.0, 2000000.0},
{LETTER_G, 4600000.0, -40.0, -48.0, 4000000.0},
{LETTER_H, 5500000.0, -32.0, -40.0, 4000000.0},
{LETTER_J, 6400000.0, -24.0, -32.0, 6000000.0},
{LETTER_K, 7300000.0, -16.0, -24.0, 6000000.0},
{LETTER_L, 8200000.0, -8.0, -16.0, 8000000.0},
{LETTER_M, 9100000.0, 0.0, -8.0, 8000000.0},
{LETTER_N, 0.0, 8.0, 0.0, 0.0},
{LETTER_P, 800000.0, 16.0, 8.0, 0.0},
{LETTER_Q, 1700000.0, 24.0, 16.0, 0.0},
{LETTER_R, 2600000.0, 32.0, 24.0, 2000000.0},
{LETTER_S, 3500000.0, 40.0, 32.0, 2000000.0},
{LETTER_T, 4400000.0, 48.0, 40.0, 4000000.0},
{LETTER_U, 5300000.0, 56.0, 48.0, 4000000.0},
{LETTER_V, 6200000.0, 64.0, 56.0, 6000000.0},
{LETTER_W, 7000000.0, 72.0, 64.0, 6000000.0},
{LETTER_X, 7900000.0, 84.5, 72.0, 6000000.0}};
typedef struct UPS_Constant_Value
{
long letter; /* letter representing latitude band */
long ltr2_low_value; /* 2nd letter range - low number */
long ltr2_high_value; /* 2nd letter range - high number */
long ltr3_high_value; /* 3rd letter range - high number (UPS) */
double false_easting; /* False easting based on 2nd letter */
double false_northing; /* False northing based on 3rd letter */
} UPS_Constant;
static const UPS_Constant UPS_Constant_Table[4] =
{{LETTER_A, LETTER_J, LETTER_Z, LETTER_Z, 800000.0, 800000.0},
{LETTER_B, LETTER_A, LETTER_R, LETTER_Z, 2000000.0, 800000.0},
{LETTER_Y, LETTER_J, LETTER_Z, LETTER_P, 800000.0, 1300000.0},
{LETTER_Z, LETTER_A, LETTER_J, LETTER_P, 2000000.0, 1300000.0}};
/***************************************************************************/
/*
* FUNCTIONS
*/
long Get_Latitude_Band_Min_Northing(long letter, double* min_northing, double* northing_offset)
/*
* The function Get_Latitude_Band_Min_Northing receives a latitude band letter
* and uses the Latitude_Band_Table to determine the minimum northing and northing offset
* for that latitude band letter.
*
* letter : Latitude band letter (input)
* min_northing : Minimum northing for that letter (output)
*/
{ /* Get_Latitude_Band_Min_Northing */
long error_code = MGRS_NO_ERROR;
if ((letter >= LETTER_C) && (letter <= LETTER_H))
{
*min_northing = Latitude_Band_Table[letter-2].min_northing;
*northing_offset = Latitude_Band_Table[letter-2].northing_offset;
}
else if ((letter >= LETTER_J) && (letter <= LETTER_N))
{
*min_northing = Latitude_Band_Table[letter-3].min_northing;
*northing_offset = Latitude_Band_Table[letter-3].northing_offset;
}
else if ((letter >= LETTER_P) && (letter <= LETTER_X))
{
*min_northing = Latitude_Band_Table[letter-4].min_northing;
*northing_offset = Latitude_Band_Table[letter-4].northing_offset;
}
else
error_code |= MGRS_STRING_ERROR;
return error_code;
} /* Get_Latitude_Band_Min_Northing */
long Get_Latitude_Range(long letter, double* north, double* south)
/*
* The function Get_Latitude_Range receives a latitude band letter
* and uses the Latitude_Band_Table to determine the latitude band
* boundaries for that latitude band letter.
*
* letter : Latitude band letter (input)
* north : Northern latitude boundary for that letter (output)
* north : Southern latitude boundary for that letter (output)
*/
{ /* Get_Latitude_Range */
long error_code = MGRS_NO_ERROR;
if ((letter >= LETTER_C) && (letter <= LETTER_H))
{
*north = Latitude_Band_Table[letter-2].north * DEG_TO_RAD;
*south = Latitude_Band_Table[letter-2].south * DEG_TO_RAD;
}
else if ((letter >= LETTER_J) && (letter <= LETTER_N))
{
*north = Latitude_Band_Table[letter-3].north * DEG_TO_RAD;
*south = Latitude_Band_Table[letter-3].south * DEG_TO_RAD;
}
else if ((letter >= LETTER_P) && (letter <= LETTER_X))
{
*north = Latitude_Band_Table[letter-4].north * DEG_TO_RAD;
*south = Latitude_Band_Table[letter-4].south * DEG_TO_RAD;
}
else
error_code |= MGRS_STRING_ERROR;
return error_code;
} /* Get_Latitude_Range */
long Get_Latitude_Letter(double latitude, int* letter)
/*
* The function Get_Latitude_Letter receives a latitude value
* and uses the Latitude_Band_Table to determine the latitude band
* letter for that latitude.
*
* latitude : Latitude (input)
* letter : Latitude band letter (output)
*/
{ /* Get_Latitude_Letter */
double temp = 0.0;
long error_code = MGRS_NO_ERROR;
double lat_deg = latitude * RAD_TO_DEG;
if (lat_deg >= 72 && lat_deg < 84.5)
*letter = LETTER_X;
else if (lat_deg > -80.5 && lat_deg < 72)
{
temp = ((latitude + (80.0 * DEG_TO_RAD)) / (8.0 * DEG_TO_RAD)) + 1.0e-12;
*letter = Latitude_Band_Table[(int)temp].letter;
}
else
error_code |= MGRS_LAT_ERROR;
return error_code;
} /* Get_Latitude_Letter */
long Check_Zone(char* MGRS, long* zone_exists)
/*
* The function Check_Zone receives an MGRS coordinate string.
* If a zone is given, TRUE is returned. Otherwise, FALSE
* is returned.
*
* MGRS : MGRS coordinate string (input)
* zone_exists : TRUE if a zone is given,
* FALSE if a zone is not given (output)
*/
{ /* Check_Zone */
int i = 0;
int j = 0;
int num_digits = 0;
long error_code = MGRS_NO_ERROR;
/* skip any leading blanks */
while (MGRS[i] == ' ')
i++;
j = i;
while (isdigit(MGRS[i]))
i++;
num_digits = i - j;
if (num_digits <= 2)
if (num_digits > 0)
*zone_exists = TRUE;
else
*zone_exists = FALSE;
else
error_code |= MGRS_STRING_ERROR;
return error_code;
} /* Check_Zone */
long Round_MGRS (double value)
/*
* The function Round_MGRS rounds the input value to the
* nearest integer, using the standard engineering rule.
* The rounded integer value is then returned.
*
* value : Value to be rounded (input)
*/
{ /* Round_MGRS */
double ivalue;
long ival;
double fraction = modf (value, &ivalue);
ival = (long)(ivalue);
if ((fraction > 0.5) || ((fraction == 0.5) && (ival%2 == 1)))
ival++;
return (ival);
} /* Round_MGRS */
long Make_MGRS_String (char* MGRS,
long Zone,
int Letters[MGRS_LETTERS],
double Easting,
double Northing,
long Precision)
/*
* The function Make_MGRS_String constructs an MGRS string
* from its component parts.
*
* MGRS : MGRS coordinate string (output)
* Zone : UTM Zone (input)
* Letters : MGRS coordinate string letters (input)
* Easting : Easting value (input)
* Northing : Northing value (input)
* Precision : Precision level of MGRS string (input)
*/
{ /* Make_MGRS_String */
long i;
long j;
double divisor;
long east;
long north;
char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
long error_code = MGRS_NO_ERROR;
i = 0;
if (Zone)
i = sprintf (MGRS+i,"%2.2ld",Zone);
else
strncpy(MGRS, " ", 2); // 2 spaces
for (j=0;j<3;j++)
MGRS[i++] = alphabet[Letters[j]];
divisor = pow (10.0, (5 - Precision));
Easting = fmod (Easting, 100000.0);
if (Easting >= 99999.5)
Easting = 99999.0;
east = (long)(Easting/divisor);
i += sprintf (MGRS+i, "%*.*ld", (int)Precision, (int)Precision, east);
Northing = fmod (Northing, 100000.0);
if (Northing >= 99999.5)
Northing = 99999.0;
north = (long)(Northing/divisor);
i += sprintf (MGRS+i, "%*.*ld", (int)Precision, (int)Precision, north);
return (error_code);
} /* Make_MGRS_String */
long Break_MGRS_String (char* MGRS,
long* Zone,
long Letters[MGRS_LETTERS],
double* Easting,
double* Northing,
long* Precision)
/*
* The function Break_MGRS_String breaks down an MGRS
* coordinate string into its component parts.
*
* MGRS : MGRS coordinate string (input)
* Zone : UTM Zone (output)
* Letters : MGRS coordinate string letters (output)
* Easting : Easting value (output)
* Northing : Northing value (output)
* Precision : Precision level of MGRS string (output)
*/
{ /* Break_MGRS_String */
long num_digits;
long num_letters;
long i = 0;
long j = 0;
long error_code = MGRS_NO_ERROR;
while (MGRS[i] == ' ')
i++; /* skip any leading blanks */
j = i;
while (isdigit(MGRS[i]))
i++;
num_digits = i - j;
if (num_digits <= 2)
if (num_digits > 0)
{
char zone_string[3];
/* get zone */
strncpy (zone_string, MGRS+j, 2);
zone_string[2] = 0;
sscanf (zone_string, "%ld", Zone);
if ((*Zone < 1) || (*Zone > 60))
error_code |= MGRS_STRING_ERROR;
}
else
*Zone = 0;
else
error_code |= MGRS_STRING_ERROR;
j = i;
while (isalpha(MGRS[i]))
i++;
num_letters = i - j;
if (num_letters == 3)
{
/* get letters */
Letters[0] = (toupper(MGRS[j]) - (long)'A');
if ((Letters[0] == LETTER_I) || (Letters[0] == LETTER_O))
error_code |= MGRS_STRING_ERROR;
Letters[1] = (toupper(MGRS[j+1]) - (long)'A');
if ((Letters[1] == LETTER_I) || (Letters[1] == LETTER_O))
error_code |= MGRS_STRING_ERROR;
Letters[2] = (toupper(MGRS[j+2]) - (long)'A');
if ((Letters[2] == LETTER_I) || (Letters[2] == LETTER_O))
error_code |= MGRS_STRING_ERROR;
}
else
error_code |= MGRS_STRING_ERROR;
j = i;
while (isdigit(MGRS[i]))
i++;
num_digits = i - j;
if ((num_digits <= 10) && (num_digits%2 == 0))
{
long n;
char east_string[6];
char north_string[6];
long east;
long north;
double multiplier;
/* get easting & northing */
n = num_digits/2;
*Precision = n;
if (n > 0)
{
strncpy (east_string, MGRS+j, n);
east_string[n] = 0;
sscanf (east_string, "%ld", &east);
strncpy (north_string, MGRS+j+n, n);
north_string[n] = 0;
sscanf (north_string, "%ld", &north);
multiplier = pow (10.0, 5 - n);
*Easting = east * multiplier;
*Northing = north * multiplier;
}
else
{
*Easting = 0.0;
*Northing = 0.0;
}
}
else
error_code |= MGRS_STRING_ERROR;
return (error_code);
} /* Break_MGRS_String */
void Get_Grid_Values (long zone,
long* ltr2_low_value,
long* ltr2_high_value,
double *pattern_offset)
/*
* The function getGridValues sets the letter range used for
* the 2nd letter in the MGRS coordinate string, based on the set
* number of the utm zone. It also sets the pattern offset using a
* value of A for the second letter of the grid square, based on
* the grid pattern and set number of the utm zone.
*
* zone : Zone number (input)
* ltr2_low_value : 2nd letter low number (output)
* ltr2_high_value : 2nd letter high number (output)
* pattern_offset : Pattern offset (output)
*/
{ /* BEGIN Get_Grid_Values */
long set_number; /* Set number (1-6) based on UTM zone number */
long aa_pattern; /* Pattern based on ellipsoid code */
set_number = zone % 6;
if (!set_number)
set_number = 6;
if (!strcmp(MGRS_Ellipsoid_Code,CLARKE_1866) || !strcmp(MGRS_Ellipsoid_Code, CLARKE_1880) ||
!strcmp(MGRS_Ellipsoid_Code,BESSEL_1841) || !strcmp(MGRS_Ellipsoid_Code,BESSEL_1841_NAMIBIA))
aa_pattern = FALSE;
else
aa_pattern = TRUE;
if ((set_number == 1) || (set_number == 4))
{
*ltr2_low_value = LETTER_A;
*ltr2_high_value = LETTER_H;
}
else if ((set_number == 2) || (set_number == 5))
{
*ltr2_low_value = LETTER_J;
*ltr2_high_value = LETTER_R;
}
else if ((set_number == 3) || (set_number == 6))
{
*ltr2_low_value = LETTER_S;
*ltr2_high_value = LETTER_Z;
}
/* False northing at A for second letter of grid square */
if (aa_pattern)
{
if ((set_number % 2) == 0)
*pattern_offset = 500000.0;
else
*pattern_offset = 0.0;
}
else
{
if ((set_number % 2) == 0)
*pattern_offset = 1500000.0;
else
*pattern_offset = 1000000.00;
}
} /* END OF Get_Grid_Values */
long UTM_To_MGRS (long Zone,
char Hemisphere,
double Longitude,
double Latitude,
double Easting,
double Northing,
long Precision,
char *MGRS)
/*
* The function UTM_To_MGRS calculates an MGRS coordinate string
* based on the zone, latitude, easting and northing.
*
* Zone : Zone number (input)
* Hemisphere: Hemisphere (input)
* Longitude : Longitude in radians (input)
* Latitude : Latitude in radians (input)
* Easting : Easting (input)
* Northing : Northing (input)
* Precision : Precision (input)
* MGRS : MGRS coordinate string (output)
*/
{ /* BEGIN UTM_To_MGRS */
double pattern_offset; /* Northing offset for 3rd letter */
double grid_easting; /* Easting used to derive 2nd letter of MGRS */
double grid_northing; /* Northing used to derive 3rd letter of MGRS */
long ltr2_low_value; /* 2nd letter range - low number */
long ltr2_high_value; /* 2nd letter range - high number */
int letters[MGRS_LETTERS]; /* Number location of 3 letters in alphabet */
double divisor;
double rounded_easting;
long temp_error_code = MGRS_NO_ERROR;
long error_code = MGRS_NO_ERROR;
divisor = pow (10.0, (5 - Precision));
rounded_easting = Round_MGRS (Easting/divisor) * divisor;
/* Special check for rounding to (truncated) eastern edge of zone 31V */
if ((Zone == 31) && (((Latitude >= 56.0 * DEG_TO_RAD) && (Latitude < 64.0 * DEG_TO_RAD)) && ((Longitude >= 3.0 * DEG_TO_RAD) || (rounded_easting >= 500000.0))))
{ /* Reconvert to UTM zone 32 */
Set_UTM_Parameters (MGRS_a, MGRS_f, 32);
temp_error_code = Convert_Geodetic_To_UTM (Latitude, Longitude, &Zone, &Hemisphere, &Easting, &Northing);
if(temp_error_code)
{
if(temp_error_code & UTM_LAT_ERROR)
error_code |= MGRS_LAT_ERROR;
if(temp_error_code & UTM_LON_ERROR)
error_code |= MGRS_LON_ERROR;
if(temp_error_code & UTM_ZONE_OVERRIDE_ERROR)
error_code |= MGRS_ZONE_ERROR;
if(temp_error_code & UTM_EASTING_ERROR)
error_code |= MGRS_EASTING_ERROR;
if(temp_error_code & UTM_NORTHING_ERROR)
error_code |= MGRS_NORTHING_ERROR;
return error_code;
}
else
/* Round easting value using new easting */
Easting = Round_MGRS (Easting/divisor) * divisor;
}
else
Easting = rounded_easting;
/* Round northing values */
Northing = Round_MGRS (Northing/divisor) * divisor;
if( Latitude <= 0.0 && Northing == 1.0e7)
{
Latitude = 0.0;
Northing = 0.0;
}
Get_Grid_Values(Zone, &ltr2_low_value, &ltr2_high_value, &pattern_offset);
error_code = Get_Latitude_Letter(Latitude, &letters[0]);
if (!error_code)
{
grid_northing = Northing;
while (grid_northing >= TWOMIL)
{
grid_northing = grid_northing - TWOMIL;
}
grid_northing = grid_northing + pattern_offset;
if(grid_northing >= TWOMIL)
grid_northing = grid_northing - TWOMIL;
letters[2] = (long)(grid_northing / ONEHT);
if (letters[2] > LETTER_H)
letters[2] = letters[2] + 1;
if (letters[2] > LETTER_N)
letters[2] = letters[2] + 1;
grid_easting = Easting;
if (((letters[0] == LETTER_V) && (Zone == 31)) && (grid_easting == 500000.0))
grid_easting = grid_easting - 1.0; /* SUBTRACT 1 METER */
letters[1] = ltr2_low_value + ((long)(grid_easting / ONEHT) -1);
if ((ltr2_low_value == LETTER_J) && (letters[1] > LETTER_N))
letters[1] = letters[1] + 1;
Make_MGRS_String (MGRS, Zone, letters, grid_easting, Northing, Precision);
}
return error_code;
} /* END UTM_To_MGRS */
long Set_MGRS_Parameters (double a,
double f,
char *Ellipsoid_Code)
/*
* The function SET_MGRS_PARAMETERS receives the ellipsoid parameters and sets
* the corresponding state variables. If any errors occur, the error code(s)
* are returned by the function, otherwise MGRS_NO_ERROR is returned.
*
* a : Semi-major axis of ellipsoid in meters (input)
* f : Flattening of ellipsoid (input)
* Ellipsoid_Code : 2-letter code for ellipsoid (input)
*/
{ /* Set_MGRS_Parameters */
double inv_f = 1 / f;
long Error_Code = MGRS_NO_ERROR;
if (a <= 0.0)
{ /* Semi-major axis must be greater than zero */
Error_Code |= MGRS_A_ERROR;
}
if ((inv_f < 250) || (inv_f > 350))
{ /* Inverse flattening must be between 250 and 350 */
Error_Code |= MGRS_INV_F_ERROR;
}
if (!Error_Code)
{ /* no errors */
MGRS_a = a;
MGRS_f = f;
strcpy (MGRS_Ellipsoid_Code, Ellipsoid_Code);
}
return (Error_Code);
} /* Set_MGRS_Parameters */
void Get_MGRS_Parameters (double *a,
double *f,
char* Ellipsoid_Code)
/*
* The function Get_MGRS_Parameters returns the current ellipsoid
* parameters.
*
* a : Semi-major axis of ellipsoid, in meters (output)
* f : Flattening of ellipsoid (output)
* Ellipsoid_Code : 2-letter code for ellipsoid (output)
*/
{ /* Get_MGRS_Parameters */
*a = MGRS_a;
*f = MGRS_f;
strcpy (Ellipsoid_Code, MGRS_Ellipsoid_Code);
return;
} /* Get_MGRS_Parameters */
long Convert_Geodetic_To_MGRS (double Latitude,
double Longitude,
long Precision,
char* MGRS)
/*
* The function Convert_Geodetic_To_MGRS converts Geodetic (latitude and
* longitude) coordinates to an MGRS coordinate string, according to the
* current ellipsoid parameters. If any errors occur, the error code(s)
* are returned by the function, otherwise MGRS_NO_ERROR is returned.
*
* Latitude : Latitude in radians (input)
* Longitude : Longitude in radians (input)
* Precision : Precision level of MGRS string (input)
* MGRS : MGRS coordinate string (output)
*
*/
{ /* Convert_Geodetic_To_MGRS */
long zone;
char hemisphere;
double easting;
double northing;
long temp_error_code = MGRS_NO_ERROR;
long error_code = MGRS_NO_ERROR;
if ((Latitude < -PI_OVER_2) || (Latitude > PI_OVER_2))
{ /* Latitude out of range */
error_code |= MGRS_LAT_ERROR;
}
if ((Longitude < -PI) || (Longitude > (2*PI)))
{ /* Longitude out of range */
error_code |= MGRS_LON_ERROR;
}
if ((Precision < 0) || (Precision > MAX_PRECISION))
error_code |= MGRS_PRECISION_ERROR;
if (!error_code)
{
if ((Latitude < MIN_UTM_LAT) || (Latitude > MAX_UTM_LAT))
{
temp_error_code = Set_UPS_Parameters (MGRS_a, MGRS_f);
if(!temp_error_code)
{
temp_error_code = Convert_Geodetic_To_UPS (Latitude, Longitude, &hemisphere, &easting, &northing);
if(!temp_error_code)
{
error_code |= Convert_UPS_To_MGRS (hemisphere, easting, northing, Precision, MGRS);
}
else
{
if(temp_error_code & UPS_LAT_ERROR)
error_code |= MGRS_LAT_ERROR;
if(temp_error_code & UPS_LON_ERROR)
error_code |= MGRS_LON_ERROR;
}
}
else
{
if(temp_error_code & UPS_A_ERROR)
error_code |= MGRS_A_ERROR;
if(temp_error_code & UPS_INV_F_ERROR)
error_code |= MGRS_INV_F_ERROR;
}
}
else
{
temp_error_code = Set_UTM_Parameters (MGRS_a, MGRS_f, 0);
if(!temp_error_code)
{
temp_error_code = Convert_Geodetic_To_UTM (Latitude, Longitude, &zone, &hemisphere, &easting, &northing);
if(!temp_error_code)
error_code |= UTM_To_MGRS (zone, hemisphere, Longitude, Latitude, easting, northing, Precision, MGRS);
else
{
if(temp_error_code & UTM_LAT_ERROR)
error_code |= MGRS_LAT_ERROR;
if(temp_error_code & UTM_LON_ERROR)
error_code |= MGRS_LON_ERROR;
if(temp_error_code & UTM_ZONE_OVERRIDE_ERROR)
error_code |= MGRS_ZONE_ERROR;
if(temp_error_code & UTM_EASTING_ERROR)
error_code |= MGRS_EASTING_ERROR;
if(temp_error_code & UTM_NORTHING_ERROR)
error_code |= MGRS_NORTHING_ERROR;
}
}
else
{
if(temp_error_code & UTM_A_ERROR)
error_code |= MGRS_A_ERROR;
if(temp_error_code & UTM_INV_F_ERROR)
error_code |= MGRS_INV_F_ERROR;
if(temp_error_code & UTM_ZONE_OVERRIDE_ERROR)
error_code |= MGRS_ZONE_ERROR;
}
}
}
return (error_code);
} /* Convert_Geodetic_To_MGRS */
long Convert_MGRS_To_Geodetic (char* MGRS,
double *Latitude,
double *Longitude)
/*
* The function Convert_MGRS_To_Geodetic converts an MGRS coordinate string
* to Geodetic (latitude and longitude) coordinates
* according to the current ellipsoid parameters. If any errors occur,
* the error code(s) are returned by the function, otherwise UTM_NO_ERROR
* is returned.
*
* MGRS : MGRS coordinate string (input)
* Latitude : Latitude in radians (output)
* Longitude : Longitude in radians (output)
*
*/
{ /* Convert_MGRS_To_Geodetic */
long zone;
char hemisphere;
double easting;
double northing;
long zone_exists;
long temp_error_code = MGRS_NO_ERROR;
long error_code = MGRS_NO_ERROR;
error_code = Check_Zone(MGRS, &zone_exists);
if (!error_code)
{
if (zone_exists)
{
error_code |= Convert_MGRS_To_UTM (MGRS, &zone, &hemisphere, &easting, &northing);
if(!error_code || (error_code & MGRS_LAT_WARNING))
{
temp_error_code = Set_UTM_Parameters (MGRS_a, MGRS_f, 0);
if(!temp_error_code)
{
temp_error_code = Convert_UTM_To_Geodetic (zone, hemisphere, easting, northing, Latitude, Longitude);
if(temp_error_code)
{
if((temp_error_code & UTM_ZONE_ERROR) || (temp_error_code & UTM_HEMISPHERE_ERROR))
error_code |= MGRS_STRING_ERROR;
if(temp_error_code & UTM_EASTING_ERROR)
error_code |= MGRS_EASTING_ERROR;
if(temp_error_code & UTM_NORTHING_ERROR)
error_code |= MGRS_NORTHING_ERROR;
}
}
else
{
if(temp_error_code & UTM_A_ERROR)
error_code |= MGRS_A_ERROR;
if(temp_error_code & UTM_INV_F_ERROR)
error_code |= MGRS_INV_F_ERROR;
if(temp_error_code & UTM_ZONE_OVERRIDE_ERROR)
error_code |= MGRS_ZONE_ERROR;
}
}
}
else
{
error_code |= Convert_MGRS_To_UPS (MGRS, &hemisphere, &easting, &northing);
if(!error_code)
{
temp_error_code = Set_UPS_Parameters (MGRS_a, MGRS_f);
if(!temp_error_code)
{
temp_error_code = Convert_UPS_To_Geodetic (hemisphere, easting, northing, Latitude, Longitude);
if(temp_error_code)
{
if(temp_error_code & UPS_HEMISPHERE_ERROR)
error_code |= MGRS_STRING_ERROR;
if(temp_error_code & UPS_EASTING_ERROR)
error_code |= MGRS_EASTING_ERROR;
if(temp_error_code & UPS_LAT_ERROR)
error_code |= MGRS_NORTHING_ERROR;
}
}
else
{
if(temp_error_code & UPS_A_ERROR)
error_code |= MGRS_A_ERROR;
if(temp_error_code & UPS_INV_F_ERROR)
error_code |= MGRS_INV_F_ERROR;
}
}
}
}
return (error_code);
} /* END OF Convert_MGRS_To_Geodetic */
long Convert_UTM_To_MGRS (long Zone,
char Hemisphere,
double Easting,
double Northing,
long Precision,
char* MGRS)
/*
* The function Convert_UTM_To_MGRS converts UTM (zone, easting, and
* northing) coordinates to an MGRS coordinate string, according to the
* current ellipsoid parameters. If any errors occur, the error code(s)
* are returned by the function, otherwise MGRS_NO_ERROR is returned.
*
* Zone : UTM zone (input)
* Hemisphere : North or South hemisphere (input)
* Easting : Easting (X) in meters (input)
* Northing : Northing (Y) in meters (input)
* Precision : Precision level of MGRS string (input)
* MGRS : MGRS coordinate string (output)
*/
{ /* Convert_UTM_To_MGRS */
double latitude; /* Latitude of UTM point */
double longitude; /* Longitude of UTM point */
long utm_error_code = MGRS_NO_ERROR;
long error_code = MGRS_NO_ERROR;
if ((Zone < 1) || (Zone > 60))
error_code |= MGRS_ZONE_ERROR;
if ((Hemisphere != 'S') && (Hemisphere != 'N'))
error_code |= MGRS_HEMISPHERE_ERROR;
if ((Easting < MIN_EASTING) || (Easting > MAX_EASTING))
error_code |= MGRS_EASTING_ERROR;
if ((Northing < MIN_NORTHING) || (Northing > MAX_NORTHING))
error_code |= MGRS_NORTHING_ERROR;
if ((Precision < 0) || (Precision > MAX_PRECISION))
error_code |= MGRS_PRECISION_ERROR;
if (!error_code)
{
Set_UTM_Parameters (MGRS_a, MGRS_f, 0);
utm_error_code = Convert_UTM_To_Geodetic (Zone, Hemisphere, Easting, Northing, &latitude, &longitude);
if(utm_error_code)
{
if((utm_error_code & UTM_ZONE_ERROR) || (utm_error_code & UTM_HEMISPHERE_ERROR))
error_code |= MGRS_STRING_ERROR;
if(utm_error_code & UTM_EASTING_ERROR)
error_code |= MGRS_EASTING_ERROR;
if(utm_error_code & UTM_NORTHING_ERROR)
error_code |= MGRS_NORTHING_ERROR;
}
error_code = UTM_To_MGRS (Zone, Hemisphere, longitude, latitude, Easting, Northing, Precision, MGRS);
}
return (error_code);
} /* Convert_UTM_To_MGRS */
long Convert_MGRS_To_UTM (char *MGRS,
long *Zone,
char *Hemisphere,
double *Easting,
double *Northing)
/*
* The function Convert_MGRS_To_UTM converts an MGRS coordinate string
* to UTM projection (zone, hemisphere, easting and northing) coordinates
* according to the current ellipsoid parameters. If any errors occur,
* the error code(s) are returned by the function, otherwise UTM_NO_ERROR
* is returned.
*
* MGRS : MGRS coordinate string (input)
* Zone : UTM zone (output)
* Hemisphere : North or South hemisphere (output)
* Easting : Easting (X) in meters (output)
* Northing : Northing (Y) in meters (output)
*/
{ /* Convert_MGRS_To_UTM */
double min_northing;
double northing_offset;
long ltr2_low_value;
long ltr2_high_value;
double pattern_offset;
double upper_lat_limit; /* North latitude limits based on 1st letter */
double lower_lat_limit; /* South latitude limits based on 1st letter */
double grid_easting; /* Easting for 100,000 meter grid square */
double grid_northing; /* Northing for 100,000 meter grid square */
long letters[MGRS_LETTERS];
long in_precision;
double latitude = 0.0;
double longitude = 0.0;
double divisor = 1.0;
long utm_error_code = MGRS_NO_ERROR;
long error_code = MGRS_NO_ERROR;
error_code = Break_MGRS_String (MGRS, Zone, letters, Easting, Northing, &in_precision);
if (!*Zone)
error_code |= MGRS_STRING_ERROR;
else
{
if (!error_code)
{
if ((letters[0] == LETTER_X) && ((*Zone == 32) || (*Zone == 34) || (*Zone == 36)))
error_code |= MGRS_STRING_ERROR;
else
{
if (letters[0] < LETTER_N)
*Hemisphere = 'S';
else
*Hemisphere = 'N';
Get_Grid_Values(*Zone, &ltr2_low_value, &ltr2_high_value, &pattern_offset);
/* Check that the second letter of the MGRS string is within
* the range of valid second letter values
* Also check that the third letter is valid */
if ((letters[1] < ltr2_low_value) || (letters[1] > ltr2_high_value) || (letters[2] > LETTER_V))
error_code |= MGRS_STRING_ERROR;
if (!error_code)
{
double row_letter_northing = (double)(letters[2]) * ONEHT;
grid_easting = (double)((letters[1]) - ltr2_low_value + 1) * ONEHT;
if ((ltr2_low_value == LETTER_J) && (letters[1] > LETTER_O))
grid_easting = grid_easting - ONEHT;
if (letters[2] > LETTER_O)
row_letter_northing = row_letter_northing - ONEHT;
if (letters[2] > LETTER_I)
row_letter_northing = row_letter_northing - ONEHT;
if (row_letter_northing >= TWOMIL)
row_letter_northing = row_letter_northing - TWOMIL;
error_code = Get_Latitude_Band_Min_Northing(letters[0], &min_northing, &northing_offset);
if (!error_code)
{
grid_northing = row_letter_northing - pattern_offset;
if(grid_northing < 0)
grid_northing += TWOMIL;
grid_northing += northing_offset;
if(grid_northing < min_northing)
grid_northing += TWOMIL;
*Easting = grid_easting + *Easting;
*Northing = grid_northing + *Northing;
/* check that point is within Zone Letter bounds */
utm_error_code = Set_UTM_Parameters(MGRS_a,MGRS_f,0);
if (!utm_error_code)
{
utm_error_code = Convert_UTM_To_Geodetic(*Zone,*Hemisphere,*Easting,*Northing,&latitude,&longitude);
if (!utm_error_code)
{
divisor = pow (10.0, in_precision);
error_code = Get_Latitude_Range(letters[0], &upper_lat_limit, &lower_lat_limit);
if (!error_code)
{
if (!(((lower_lat_limit - DEG_TO_RAD/divisor) <= latitude) && (latitude <= (upper_lat_limit + DEG_TO_RAD/divisor))))
error_code |= MGRS_LAT_WARNING;
}
}
else
{
if((utm_error_code & UTM_ZONE_ERROR) || (utm_error_code & UTM_HEMISPHERE_ERROR))
error_code |= MGRS_STRING_ERROR;
if(utm_error_code & UTM_EASTING_ERROR)
error_code |= MGRS_EASTING_ERROR;
if(utm_error_code & UTM_NORTHING_ERROR)
error_code |= MGRS_NORTHING_ERROR;
}
}
else
{
if(utm_error_code & UTM_A_ERROR)
error_code |= MGRS_A_ERROR;
if(utm_error_code & UTM_INV_F_ERROR)
error_code |= MGRS_INV_F_ERROR;
if(utm_error_code & UTM_ZONE_OVERRIDE_ERROR)
error_code |= MGRS_ZONE_ERROR;
}
}
}
}
}
}
return (error_code);
} /* Convert_MGRS_To_UTM */
long Convert_UPS_To_MGRS (char Hemisphere,
double Easting,
double Northing,
long Precision,
char* MGRS)
/*
* The function Convert_UPS_To_MGRS converts UPS (hemisphere, easting,
* and northing) coordinates to an MGRS coordinate string according to
* the current ellipsoid parameters. If any errors occur, the error
* code(s) are returned by the function, otherwise UPS_NO_ERROR is
* returned.
*
* Hemisphere : Hemisphere either 'N' or 'S' (input)
* Easting : Easting/X in meters (input)
* Northing : Northing/Y in meters (input)
* Precision : Precision level of MGRS string (input)
* MGRS : MGRS coordinate string (output)
*/
{ /* Convert_UPS_To_MGRS */
double false_easting; /* False easting for 2nd letter */
double false_northing; /* False northing for 3rd letter */
double grid_easting; /* Easting used to derive 2nd letter of MGRS */
double grid_northing; /* Northing used to derive 3rd letter of MGRS */
long ltr2_low_value; /* 2nd letter range - low number */
int letters[MGRS_LETTERS]; /* Number location of 3 letters in alphabet */
double divisor;
int index = 0;
long error_code = MGRS_NO_ERROR;
if ((Hemisphere != 'N') && (Hemisphere != 'S'))
error_code |= MGRS_HEMISPHERE_ERROR;
if ((Easting < MIN_EAST_NORTH) || (Easting > MAX_EAST_NORTH))
error_code |= MGRS_EASTING_ERROR;
if ((Northing < MIN_EAST_NORTH) || (Northing > MAX_EAST_NORTH))
error_code |= MGRS_NORTHING_ERROR;
if ((Precision < 0) || (Precision > MAX_PRECISION))
error_code |= MGRS_PRECISION_ERROR;
if (!error_code)
{
divisor = pow (10.0, (5 - Precision));
Easting = Round_MGRS (Easting/divisor) * divisor;
Northing = Round_MGRS (Northing/divisor) * divisor;
if (Hemisphere == 'N')
{
if (Easting >= TWOMIL)
letters[0] = LETTER_Z;
else
letters[0] = LETTER_Y;
index = letters[0] - 22;
ltr2_low_value = UPS_Constant_Table[index].ltr2_low_value;
false_easting = UPS_Constant_Table[index].false_easting;
false_northing = UPS_Constant_Table[index].false_northing;
}
else
{
if (Easting >= TWOMIL)
letters[0] = LETTER_B;
else
letters[0] = LETTER_A;
ltr2_low_value = UPS_Constant_Table[letters[0]].ltr2_low_value;
false_easting = UPS_Constant_Table[letters[0]].false_easting;
false_northing = UPS_Constant_Table[letters[0]].false_northing;
}
grid_northing = Northing;
grid_northing = grid_northing - false_northing;
letters[2] = (long)(grid_northing / ONEHT);
if (letters[2] > LETTER_H)
letters[2] = letters[2] + 1;
if (letters[2] > LETTER_N)
letters[2] = letters[2] + 1;
grid_easting = Easting;
grid_easting = grid_easting - false_easting;
letters[1] = ltr2_low_value + ((long)(grid_easting / ONEHT));
if (Easting < TWOMIL)
{
if (letters[1] > LETTER_L)
letters[1] = letters[1] + 3;
if (letters[1] > LETTER_U)
letters[1] = letters[1] + 2;
}
else
{
if (letters[1] > LETTER_C)
letters[1] = letters[1] + 2;
if (letters[1] > LETTER_H)
letters[1] = letters[1] + 1;
if (letters[1] > LETTER_L)
letters[1] = letters[1] + 3;
}
Make_MGRS_String (MGRS, 0, letters, Easting, Northing, Precision);
}
return (error_code);
} /* Convert_UPS_To_MGRS */
long Convert_MGRS_To_UPS ( char *MGRS,
char *Hemisphere,
double *Easting,
double *Northing)
/*
* The function Convert_MGRS_To_UPS converts an MGRS coordinate string
* to UPS (hemisphere, easting, and northing) coordinates, according
* to the current ellipsoid parameters. If any errors occur, the error
* code(s) are returned by the function, otherwide UPS_NO_ERROR is returned.
*
* MGRS : MGRS coordinate string (input)
* Hemisphere : Hemisphere either 'N' or 'S' (output)
* Easting : Easting/X in meters (output)
* Northing : Northing/Y in meters (output)
*/
{ /* Convert_MGRS_To_UPS */
long ltr2_high_value; /* 2nd letter range - high number */
long ltr3_high_value; /* 3rd letter range - high number (UPS) */
long ltr2_low_value; /* 2nd letter range - low number */
double false_easting; /* False easting for 2nd letter */
double false_northing; /* False northing for 3rd letter */
double grid_easting; /* easting for 100,000 meter grid square */
double grid_northing; /* northing for 100,000 meter grid square */
long zone;
long letters[MGRS_LETTERS];
long in_precision;
int index = 0;
long error_code = MGRS_NO_ERROR;
error_code = Break_MGRS_String (MGRS, &zone, letters, Easting, Northing, &in_precision);
if (zone)
error_code |= MGRS_STRING_ERROR;
else
{
if (!error_code)
{
if (letters[0] >= LETTER_Y)
{
*Hemisphere = 'N';
index = letters[0] - 22;
ltr2_low_value = UPS_Constant_Table[index].ltr2_low_value;
ltr2_high_value = UPS_Constant_Table[index].ltr2_high_value;
ltr3_high_value = UPS_Constant_Table[index].ltr3_high_value;
false_easting = UPS_Constant_Table[index].false_easting;
false_northing = UPS_Constant_Table[index].false_northing;
}
else
{
*Hemisphere = 'S';
ltr2_low_value = UPS_Constant_Table[letters[0]].ltr2_low_value;
ltr2_high_value = UPS_Constant_Table[letters[0]].ltr2_high_value;
ltr3_high_value = UPS_Constant_Table[letters[0]].ltr3_high_value;
false_easting = UPS_Constant_Table[letters[0]].false_easting;
false_northing = UPS_Constant_Table[letters[0]].false_northing;
}
/* Check that the second letter of the MGRS string is within
* the range of valid second letter values
* Also check that the third letter is valid */
if ((letters[1] < ltr2_low_value) || (letters[1] > ltr2_high_value) ||
((letters[1] == LETTER_D) || (letters[1] == LETTER_E) ||
(letters[1] == LETTER_M) || (letters[1] == LETTER_N) ||
(letters[1] == LETTER_V) || (letters[1] == LETTER_W)) ||
(letters[2] > ltr3_high_value))
error_code |= MGRS_STRING_ERROR;
if (!error_code)
{
grid_northing = (double)letters[2] * ONEHT + false_northing;
if (letters[2] > LETTER_I)
grid_northing = grid_northing - ONEHT;
if (letters[2] > LETTER_O)
grid_northing = grid_northing - ONEHT;
grid_easting = (double)((letters[1]) - ltr2_low_value) * ONEHT + false_easting;
if (ltr2_low_value != LETTER_A)
{
if (letters[1] > LETTER_L)
grid_easting = grid_easting - 300000.0;
if (letters[1] > LETTER_U)
grid_easting = grid_easting - 200000.0;
}
else
{
if (letters[1] > LETTER_C)
grid_easting = grid_easting - 200000.0;
if (letters[1] > LETTER_I)
grid_easting = grid_easting - ONEHT;
if (letters[1] > LETTER_L)
grid_easting = grid_easting - 300000.0;
}
*Easting = grid_easting + *Easting;
*Northing = grid_northing + *Northing;
}
}
}
return (error_code);
} /* Convert_MGRS_To_UPS */