123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206 |
- #!/usr/bin/python3
- """
- Created on Mar 3, 2017
- @author: 4X5DM
- @other: F4IYQ
- """
- from math import floor
- import argparse
- # Constants
- ASCII_0 = 48
- ASCII_A = 65
- ASCII_a = 97
- # argparse
- parser = 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, lon
- def 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_locator
- if __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.')
|