| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206 | #!/usr/bin/python3"""Created on Mar 3, 2017@author: 4X5DM@other: F4IYQ"""from math import floorimport argparse# ConstantsASCII_0 = 48ASCII_A = 65ASCII_a = 97# argparseparser = argparse.ArgumentParser(description='Convert QTH locator to latitude and longitude.')subparsers = parser.add_subparsers()p_square_to_location = subparsers.add_parser('square_to_location', help='Convert QTH locator to latitude and longitude')p_square_to_location.set_defaults(func='square_to_location')p_square_to_location.add_argument('qth_locator', type=str, help='QTH locator in format AA00 or AA00aa00')p_location_to_square = subparsers.add_parser('location_to_square', help='Convert latitude and longitude to QTH locator')p_location_to_square.set_defaults(func='location_to_square')p_location_to_square.add_argument('--lat', type=float, help='Latitude in decimal format')p_location_to_square.add_argument('--lon', type=float, help='Longitude in decimal format')p_test = subparsers.add_parser('test', help='Test QTH locator conversion')p_test.set_defaults(func='test')args = parser.parse_args()def square_to_location(qth_locator):    """    Converts QTH locator to latitude and longitude in decimal format.    Gets QTH locator as string.    Returns Tuple containing latitude and longitude as floats.    """    # Validate input    assert isinstance(qth_locator, str)    assert 4 <= len(qth_locator) <= 8    assert len(qth_locator) % 2 == 0    qth_locator = qth_locator.upper()    # Separate fields, squares and subsquares    # Fields    lon_field = ord(qth_locator[0]) - ASCII_A    lat_field = ord(qth_locator[1]) - ASCII_A    # Squares    lon_sq = ord(qth_locator[2]) - ASCII_0    lat_sq = ord(qth_locator[3]) - ASCII_0    # Subsquares    if len(qth_locator) >= 6:        lon_sub_sq = ord(qth_locator[4]) - ASCII_A        lat_sub_sq = ord(qth_locator[5]) - ASCII_A    else:        lon_sub_sq = 0        lat_sub_sq = 0    # Extended squares    if len(qth_locator) == 8:        lon_ext_sq = ord(qth_locator[6]) - ASCII_0        lat_ext_sq = ord(qth_locator[7]) - ASCII_0    else:        lon_ext_sq = 0        lat_ext_sq = 0    # Calculate latitude and longitude    lon = -180.0    lat = -90.0    lon += 20.0 * lon_field    lat += 10.0 * lat_field    lon += 2.0 * lon_sq    lat += 1.0 * lat_sq    lon += 5.0 / 60 * lon_sub_sq    lat += 2.5 / 60 * lat_sub_sq    lon += 0.5 / 60 * lon_ext_sq    lat += 0.25 / 60 * lat_ext_sq    return lat, londef location_to_square(lat, lon):    """    Converts latitude and longitude in decimal format to QTH locator.    Gets latitude and longitude as floats.    Returns QTH locator as string.    """    # Validate input    assert isinstance(lat, (int, float))    assert isinstance(lon, (int, float))    assert -90.0 <= lat <= 90.0    assert -180.0 <= lon <= 180.0    # Separate fields, squares and subsquares    lon += 180    lat += 90    # Fields    lon_field = int(floor(lon / 20))    lat_field = int(floor(lat / 10))    lon -= lon_field * 20    lat -= lat_field * 10    # Squares    lon_sq = int(floor(lon / 2))    lat_sq = int(floor(lat / 1))    lon -= lon_sq * 2    lat -= lat_sq * 1    # Subsquares    lon_sub_sq = int(floor(lon / (5.0 / 60)))    lat_sub_sq = int(floor(lat / (2.5 / 60)))    lon -= lon_sub_sq * (5.0 / 60)    lat -= lat_sub_sq * (2.5 / 60)    # Extended squares    lon_ext_sq = int(round(lon / (0.5 / 60)))    lat_ext_sq = int(round(lat / (0.25 / 60)))    qth_locator = f'{chr(lon_field + ASCII_A)}'    qth_locator += chr(lat_field + ASCII_A)    qth_locator += chr(lon_sq + ASCII_0)    qth_locator += chr(lat_sq + ASCII_0)    if lon_sub_sq > 0 or lat_sub_sq > 0 or lon_ext_sq > 0 or lat_ext_sq > 0:        qth_locator += chr(lon_sub_sq + ASCII_a)        qth_locator += chr(lat_sub_sq + ASCII_a)    if lon_ext_sq > 0 or lat_ext_sq > 0:        qth_locator += chr(lon_ext_sq + ASCII_0)        qth_locator += chr(lat_ext_sq + ASCII_0)    return qth_locatorif __name__ == '__main__':    if args.func == 'test':        print('Testing QTH Locator conversion...')        squares = [        ('JJ00', (0, 0)),        ('KM32', (32, 26)),        ('KM32jn07', (32.570831, 26.750003)),        ('KM72kk55', (32.437487, 34.875015)),        ('JN45fo', (45.603333, 8.456667)),        ('KO92so', (52.601484, 39.565160)),        ('KM72jb', (32.071209, 34.780089)),        # ('', (0, 0)),        # ('', (0, 0)),        ]        print('\nQTH locator to coordinates:')        for sq, res in squares:            loc = square_to_location(sq)            print(f'{sq}: {res}, calculated: {loc}')        print('\nCoordinates to QTH locator:')        for sq, loc in squares:            qth = location_to_square(loc[0], loc[1])            print(f'{loc}: {sq}, calculated: {qth}')    if args.func == 'square_to_location':        qth = args.qth_locator        lat, lon = square_to_location(qth)        print(f'QTH locator: {qth}, calculated coordinates: {lat}, {lon}')        exit(0)    if args.func == 'location_to_square':        lat = args.lat        lon = args.lon        if lat is None or lon is None:            parser.print_help()            exit(1)        if lat == 0 and lon == 0:            print('Error: latitude and longitude cannot be both zero.')            exit(1)        if lat < -90 or lat > 90:            print('Error: latitude must be between -90 and 90.')            exit(1)        if lon < -180 or lon > 180:            print('Error: longitude must be between -180 and 180.')            exit(1)        qth = location_to_square(lat, lon).upper()        print(qth)        exit(0)    print('\nDone.')
 |