Compare commits

...

7 commits

Author SHA1 Message Date
Tobias Wellnitz, DH1TW 8c15ab8c2c changed documents theme to readthedocs
Some checks failed
Linux / Ubuntu 24.04 - Python ${{ matrix.python-version }} (3.10, 7) (push) Has been cancelled
Linux / Ubuntu 24.04 - Python ${{ matrix.python-version }} (3.11, 7) (push) Has been cancelled
Linux / Ubuntu 24.04 - Python ${{ matrix.python-version }} (3.12, 7) (push) Has been cancelled
Linux / Ubuntu 24.04 - Python ${{ matrix.python-version }} (3.13, 7) (push) Has been cancelled
Linux / Ubuntu 24.04 - Python ${{ matrix.python-version }} (3.8, 7) (push) Has been cancelled
Linux / Ubuntu 24.04 - Python ${{ matrix.python-version }} (3.9, 7) (push) Has been cancelled
Linux / Ubuntu 24.04 - Python ${{ matrix.python-version }} (pypy3.10, 7) (push) Has been cancelled
Linux / Ubuntu 24.04 - Python ${{ matrix.python-version }} (pypy3.8, 7) (push) Has been cancelled
Linux / Ubuntu 24.04 - Python ${{ matrix.python-version }} (pypy3.9, 7) (push) Has been cancelled
Linux / MacOS 15 - Python ${{ matrix.python-version }} (3.10, 7.2) (push) Has been cancelled
Linux / MacOS 15 - Python ${{ matrix.python-version }} (3.11, 7.2) (push) Has been cancelled
Linux / MacOS 15 - Python ${{ matrix.python-version }} (3.12, 7.2) (push) Has been cancelled
Linux / MacOS 15 - Python ${{ matrix.python-version }} (3.13, 7.2) (push) Has been cancelled
Linux / MacOS 15 - Python ${{ matrix.python-version }} (3.8, 7.2) (push) Has been cancelled
Linux / MacOS 15 - Python ${{ matrix.python-version }} (3.9, 7.2) (push) Has been cancelled
Linux / MacOS 15 - Python ${{ matrix.python-version }} (pypy3.10, 7.2) (push) Has been cancelled
Linux / MacOS 15 - Python ${{ matrix.python-version }} (pypy3.8, 7.2) (push) Has been cancelled
Linux / MacOS 15 - Python ${{ matrix.python-version }} (pypy3.9, 7.2) (push) Has been cancelled
Linux / Windows latest - Python ${{ matrix.python-version }} (3.10) (push) Has been cancelled
Linux / Windows latest - Python ${{ matrix.python-version }} (3.11) (push) Has been cancelled
Linux / Windows latest - Python ${{ matrix.python-version }} (3.12) (push) Has been cancelled
Linux / Windows latest - Python ${{ matrix.python-version }} (3.13) (push) Has been cancelled
Linux / Windows latest - Python ${{ matrix.python-version }} (3.8) (push) Has been cancelled
Linux / Windows latest - Python ${{ matrix.python-version }} (3.9) (push) Has been cancelled
2025-09-10 23:21:34 +02:00
Tobias Wellnitz, DH1TW 8ef752e3ad added unittest for frequency conversion (2.4GHz - 13cm) 2025-07-30 00:49:38 +02:00
Tobias Wellnitz, DH1TW 9c61f75c28 release version 0.12.0 2025-06-10 01:34:16 +02:00
Tobias Wellnitz, DH1TW b51b32e575 updated CI build system 2025-06-10 01:32:11 +02:00
Tobias Wellnitz, DH1TW a12616ceca added missing packages for requirements-pytest 2025-06-09 23:29:42 +02:00
Dawid 1a79467db1
Add higher uWave bands and 10 char locators support (#32) 2025-06-09 23:21:27 +02:00
Tobias Wellnitz, DH1TW 4bdaf8d335 updated readme & license 2025-03-02 00:10:44 +01:00
13 changed files with 105 additions and 78 deletions

View file

@ -3,61 +3,17 @@ name: Linux
on: [push, pull_request] on: [push, pull_request]
jobs: jobs:
test_linux_legacy:
# Ubuntu 20.04 is still required for python 3.6; this doesn't work on Ubuntu 22.04 anymore
runs-on: "ubuntu-20.04"
name: "Ubuntu 20.04 - Python ${{ matrix.python-version }}"
strategy:
matrix:
python-version: ["3.6"]
redis-version: [6]
steps:
- uses: "actions/checkout@v3"
- uses: "actions/setup-python@v4"
with:
python-version: "${{ matrix.python-version }}"
cache: "pip"
cache-dependency-path: |
**/setup.py
**/requirements*.txt
- name: "Install dependencies"
run: |
set -xe
sudo apt-get install -y libxml2-dev libxslt-dev
python -VV
python -m pip install --upgrade pip setuptools
python -m pip install -e .
python -m pip install -r requirements-pytest.txt
- name: Start Redis
uses: supercharge/redis-github-action@1.2.0
with:
redis-version: ${{ matrix.redis-version }}
- name: "Run tests for ${{ matrix.python-version }}"
env:
CLUBLOG_APIKEY: ${{ secrets.CLUBLOG_APIKEY }}
QRZ_USERNAME: ${{ secrets.QRZ_USERNAME }}
QRZ_PWD: ${{ secrets.QRZ_PWD }}
PYTHON_VERSION: ${{ matrix.python-version }}
# delay the execution randomly by a couple of seconds to reduce the amount
# of concurrent API calls on Clublog and QRZ.com when all CI jobs execute simultaneously
run: |
sleep $[ ( $RANDOM % 10 ) + 1 ]s
pytest ./test
test_linux: test_linux:
runs-on: "ubuntu-22.04" runs-on: "ubuntu-24.04"
name: "Ubuntu 22.04 - Python ${{ matrix.python-version }}" name: "Ubuntu 24.04 - Python ${{ matrix.python-version }}"
env: env:
USING_COVERAGE: '3.11' USING_COVERAGE: '3.11'
strategy: strategy:
matrix: matrix:
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "pypy3.8", "pypy3.9", "pypy3.10"] python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "pypy3.8", "pypy3.9", "pypy3.10"]
redis-version: [6] redis-version: [7]
steps: steps:
- uses: "actions/checkout@v3" - uses: "actions/checkout@v3"
@ -93,7 +49,7 @@ jobs:
# delay the execution randomly by a couple of seconds to reduce the amount # delay the execution randomly by a couple of seconds to reduce the amount
# of concurrent API calls on Clublog and QRZ.com when all CI jobs execute simultaneously # of concurrent API calls on Clublog and QRZ.com when all CI jobs execute simultaneously
run: | run: |
sleep $[ ( $RANDOM % 10 ) + 1 ]s sleep $[ ( $RANDOM % 60 ) + 1 ]s
if [[ $PYTHON_VERSION == 3.11 ]] if [[ $PYTHON_VERSION == 3.11 ]]
then then
pytest --cov=test/ pytest --cov=test/
@ -116,13 +72,12 @@ jobs:
test_macos: test_macos:
# Ubuntu 20.04 is still required for python 3.6; this doesn't work on Ubuntu 22.04 anymore runs-on: "macos-15"
runs-on: "macos-12" name: "MacOS 15 - Python ${{ matrix.python-version }}"
name: "MacOS 12 - Python ${{ matrix.python-version }}"
strategy: strategy:
matrix: matrix:
python-version: ["3.6", "3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "pypy3.8", "pypy3.9", "pypy3.10"] python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "pypy3.8", "pypy3.9", "pypy3.10"]
redis-version: [6] redis-version: [7.2]
steps: steps:
- uses: "actions/checkout@v3" - uses: "actions/checkout@v3"
@ -143,7 +98,7 @@ jobs:
python -m pip install -r requirements-pytest.txt python -m pip install -r requirements-pytest.txt
- name: Start Redis - name: Start Redis
uses: shogo82148/actions-setup-redis@v1.31.1 uses: shogo82148/actions-setup-redis@v1
with: with:
redis-version: ${{ matrix.redis-version }} redis-version: ${{ matrix.redis-version }}
@ -156,16 +111,16 @@ jobs:
# delay the execution randomly by a couple of seconds to reduce the amount # delay the execution randomly by a couple of seconds to reduce the amount
# of concurrent API calls on Clublog and QRZ.com when all CI jobs execute simultaneously # of concurrent API calls on Clublog and QRZ.com when all CI jobs execute simultaneously
run: | run: |
sleep $[ ( $RANDOM % 10 ) + 1 ] sleep $[ ( $RANDOM % 60 ) + 1 ]
pytest ./test pytest ./test
test_windows: test_windows:
runs-on: "windows-latest" runs-on: "windows-2022"
name: "Windows latest - Python ${{ matrix.python-version }}" name: "Windows latest - Python ${{ matrix.python-version }}"
strategy: strategy:
matrix: matrix:
python-version: ["3.6", "3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
steps: steps:
- uses: "actions/checkout@v3" - uses: "actions/checkout@v3"
@ -193,8 +148,10 @@ jobs:
C:\msys64\usr\bin\wget.exe https://github.com/redis-windows/redis-windows/releases/download/7.0.14/Redis-7.0.14-Windows-x64-msys2-with-Service.zip C:\msys64\usr\bin\wget.exe https://github.com/redis-windows/redis-windows/releases/download/7.0.14/Redis-7.0.14-Windows-x64-msys2-with-Service.zip
C:\msys64\usr\bin\pacman.exe -S --noconfirm unzip C:\msys64\usr\bin\pacman.exe -S --noconfirm unzip
C:\msys64\usr\bin\unzip.exe Redis-7.0.14-Windows-x64-msys2-with-Service.zip C:\msys64\usr\bin\unzip.exe Redis-7.0.14-Windows-x64-msys2-with-Service.zip
sc.exe create Redis binpath=D:\a\pyhamtools\pyhamtools\Redis-7.0.14-Windows-x64-msys2-with-Service\RedisService.exe start= auto sc.exe create Redis binpath=${{ github.workspace }}\Redis-7.0.14-Windows-x64-msys2-with-Service\RedisService.exe start= auto
echo "Redis service created, now starting it"
net start Redis net start Redis
echo "Redis service started"
- name: "Run tests for ${{ matrix.python-version }}" - name: "Run tests for ${{ matrix.python-version }}"
env: env:
@ -207,5 +164,5 @@ jobs:
# amount of concurrent API calls on Clublog and QRZ.com # amount of concurrent API calls on Clublog and QRZ.com
# when all CI jobs execute simultaneously # when all CI jobs execute simultaneously
run: | run: |
start-sleep -Seconds (5..20 | get-random) start-sleep -Seconds (5..60 | get-random)
pytest pytest

View file

@ -1,6 +1,6 @@
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2014 Tobias Wellnitz Copyright (c) 2025 Tobias Wellnitz
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View file

@ -34,20 +34,22 @@ This Library is used in production at the [DXHeat.com DX Cluster](https://dxheat
Pyhamtools is compatible with Python >=3.6. Pyhamtools is compatible with Python >=3.6.
We check compatibility on OSX, Windows, and Linux with the following Python versions: We check compatibility on OSX, Windows, and Linux with the following Python versions:
* Python 3.6 (will be deprecated in 2024)
* Python 3.7 (will be deprecated in 2024)
* Python 3.8 * Python 3.8
* Python 3.9 * Python 3.9
* Python 3.10 * Python 3.10
* Python 3.11 * Python 3.11
* Python 3.12 * Python 3.12
* [pypy3.7](https://pypy.org/) (will be deprecated in 2024) * Python 3.13
* [pypy3.8](https://pypy.org/) * [pypy3.8](https://pypy.org/)
* [pypy3.9](https://pypy.org/) * [pypy3.9](https://pypy.org/)
* [pypy3.10](https://pypy.org/) * [pypy3.10](https://pypy.org/)
### depreciated: Python 2.7 & Python 3.5
The support for Python 2.7 and 3.5 has been deprecated at the end of 2023. The last version which supports Python 2.7 and Python 3.5 is 0.8.7. The support for Python 2.7 and 3.5 has been deprecated at the end of 2023. The last version which supports Python 2.7 and Python 3.5 is 0.8.7.
### depricated: Python 3.6 & Python 3.7
Support for Python 3.6 and Python 3.7 has been deprecated in June 2025. The last version which support Python 3.6 and Python 3.7 is 0.11.0.
## Documentation ## Documentation
Check out the full documentation including the changelog at: Check out the full documentation including the changelog at:

View file

@ -1,6 +1,18 @@
Changelog Changelog
--------- ---------
PyHamtools 0.12.0
================
09. June 2025
* deprecated support for Python 3.6
* deprecated support for Python 3.7
* added support for higher Microwave bands (tnx @sq6emm)
* added support for 10 characters Maidenhead locators (tnx @sq6emm)
* updated CI pipeline
PyHamtools 0.11.0 PyHamtools 0.11.0
================ ================

View file

@ -25,6 +25,7 @@ from pyhamtools.version import __version__, __release__
extensions = [ extensions = [
'sphinx.ext.autodoc', 'sphinx.ext.autodoc',
'sphinx.ext.napoleon', 'sphinx.ext.napoleon',
'sphinx_rtd_dark_mode',
] ]
# Add any paths that contain templates here, relative to this directory. # Add any paths that contain templates here, relative to this directory.
@ -95,7 +96,9 @@ pygments_style = 'sphinx'
# The theme to use for HTML and HTML Help pages. See the documentation for # The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes. # a list of builtin themes.
html_theme = 'default' # html_theme = 'default'
html_theme = 'sphinx_rtd_theme'
# html_theme = 'sphinx_material'
# Theme options are theme-specific and customize the look and feel of a theme # Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the # further. For a list of options available for each theme, see the

View file

@ -182,7 +182,7 @@ def freq_to_band(freq):
elif ((freq >= 1200000) and (freq <= 1300000)): elif ((freq >= 1200000) and (freq <= 1300000)):
band = 0.23 #23cm band = 0.23 #23cm
mode = None mode = None
elif ((freq >= 2390000) and (freq <= 2450000)): elif ((freq >= 2300000) and (freq <= 2450000)):
band = 0.13 #13cm band = 0.13 #13cm
mode = None mode = None
elif ((freq >= 3300000) and (freq <= 3500000)): elif ((freq >= 3300000) and (freq <= 3500000)):
@ -200,6 +200,18 @@ def freq_to_band(freq):
elif ((freq >= 47000000) and (freq <= 47200000)): elif ((freq >= 47000000) and (freq <= 47200000)):
band = 0.0063 #6,3mm band = 0.0063 #6,3mm
mode = None mode = None
elif ((freq >= 75500000) and (freq <= 81500000)):
band = 0.004 #4mm
mode = None
elif ((freq >= 122250000) and (freq <= 123000000)):
band = 0.0025 #2.5mm
mode = None
elif ((freq >= 134000000) and (freq <= 141000000)):
band = 0.002 #2mm
mode = None
elif ((freq >= 241000000) and (freq <= 250000000)):
band = 0.001 #1mm
mode = None
else: else:
raise KeyError raise KeyError

View file

@ -9,7 +9,7 @@ def latlong_to_locator (latitude, longitude, precision=6):
Args: Args:
latitude (float): Latitude latitude (float): Latitude
longitude (float): Longitude longitude (float): Longitude
precision (int): 4,6,8 chars (default 6) precision (int): 4,6,8,10 chars (default 6)
Returns: Returns:
string: Maidenhead locator string: Maidenhead locator
@ -33,7 +33,7 @@ def latlong_to_locator (latitude, longitude, precision=6):
""" """
if precision < 4 or precision ==5 or precision == 7 or precision > 8: if precision < 4 or precision == 5 or precision == 7 or precision == 9 or precision > 10:
return ValueError return ValueError
if longitude >= 180 or longitude <= -180: if longitude >= 180 or longitude <= -180:
@ -99,7 +99,7 @@ def locator_to_latlong (locator, center=True):
locator = locator.upper() locator = locator.upper()
if len(locator) < 4 or len(locator) == 5 or len(locator) == 7: if len(locator) < 4 or len(locator) == 5 or len(locator) == 7 or len(locator) == 9:
raise ValueError raise ValueError
if ord(locator[0]) > ord('R') or ord(locator[0]) < ord('A'): if ord(locator[0]) > ord('R') or ord(locator[0]) < ord('A'):
@ -126,6 +126,12 @@ def locator_to_latlong (locator, center=True):
if ord (locator[7]) > ord('9') or ord(locator[7]) < ord('0'): if ord (locator[7]) > ord('9') or ord(locator[7]) < ord('0'):
raise ValueError raise ValueError
if len(locator) == 10:
if ord(locator[8]) > ord('X') or ord(locator[8]) < ord('A'):
raise ValueError
if ord (locator[9]) > ord('X') or ord(locator[9]) < ord('A'):
raise ValueError
longitude = (ord(locator[0]) - ord('A')) * 20 - 180 longitude = (ord(locator[0]) - ord('A')) * 20 - 180
latitude = (ord(locator[1]) - ord('A')) * 10 - 90 latitude = (ord(locator[1]) - ord('A')) * 10 - 90
longitude += (ord(locator[2]) - ord('0')) * 2 longitude += (ord(locator[2]) - ord('0')) * 2
@ -156,6 +162,20 @@ def locator_to_latlong (locator, center=True):
longitude += 5.0 / 600 / 2 longitude += 5.0 / 600 / 2
latitude += 2.5 / 600 / 2 latitude += 2.5 / 600 / 2
elif len(locator) == 10:
longitude += (ord(locator[4]) - ord('A')) * 5.0 / 60
latitude += (ord(locator[5]) - ord('A')) * 2.5 / 60
longitude += int(locator[6]) * 5.0 / 600
latitude += int(locator[7]) * 2.5 / 600
longitude += (ord(locator[8]) - ord('A')) * 1.0 / 2880
latitude += (ord(locator[9]) - ord('A')) * 1.0 / 5760
if center:
longitude += 1.0 / 2880 / 2
latitude += 1.0 / 5760 / 2
else: else:
raise ValueError raise ValueError

View file

@ -1,3 +1,3 @@
VERSION = (0, 11, 0) VERSION = (0, 12, 0)
__release__ = ''.join(['-.'[type(x) == int]+str(x) for x in VERSION])[1:] __release__ = ''.join(['-.'[type(x) == int]+str(x) for x in VERSION])[1:]
__version__ = '.'.join((str(VERSION[0]), str(VERSION[1]))) __version__ = '.'.join((str(VERSION[0]), str(VERSION[1])))

View file

@ -1,3 +1,5 @@
sphinx>=1.8.5 sphinx>=1.8.5
sphinxcontrib-napoleon>=0.7 sphinxcontrib-napoleon>=0.7
beautifulsoup4>=4.7.1 beautifulsoup4>=4.7.1
sphinx_rtd_theme>=0.5.2
sphinx_rtd_dark_mode>=0.1.2

View file

@ -1,6 +1,9 @@
pytest>=7.0.0; python_version>='3.7' pytest>=7.0.0
pytest==4.6.11; python_version=='3.6'
pytest-blockage>=0.2.2 pytest-blockage>=0.2.2
pytest-localserver>=0.5 pytest-localserver>=0.5
pytest-cov>=2.12 pytest-cov>=2.12
maidenhead==1.6.0 maidenhead==1.6.0
requests>=2.32.4
beautifulsoup4==4.13.4
redis==5.2.1
ephem==4.2

View file

@ -23,6 +23,10 @@ class Test_latlong_to_locator():
assert latlong_to_locator(48.51760, 9.40345, precision=8) == "JN48QM84" assert latlong_to_locator(48.51760, 9.40345, precision=8) == "JN48QM84"
assert latlong_to_locator(39.222916, -86.45416, 8) == "EM69SF53" assert latlong_to_locator(39.222916, -86.45416, 8) == "EM69SF53"
def test_latlong_to_locator_10chars_precision(self):
assert latlong_to_locator(45.835677, 68.525173, precision=10) == "MN45GU30AN"
assert latlong_to_locator(51.124913, 16.941840, 10) == "JO81LC39AX"
def test_latlong_to_locator_invalid_characters(self): def test_latlong_to_locator_invalid_characters(self):
# throws ValueError in Python2 and TypeError in Python3 # throws ValueError in Python2 and TypeError in Python3

View file

@ -30,8 +30,7 @@ class Test_locator_to_latlong():
assert abs(latitude - 48.52083) < 0.00001 assert abs(latitude - 48.52083) < 0.00001
assert abs(longitude - 9.37500) < 0.00001 assert abs(longitude - 9.37500) < 0.00001
def test_locator_to_latlong_8chars(self): def test_locator_to_latlong_8chars_precision(self):
latitude, longitude = locator_to_latlong("JN48QM84") latitude, longitude = locator_to_latlong("JN48QM84")
assert abs(latitude - 48.51875) < 0.00001 assert abs(latitude - 48.51875) < 0.00001
assert abs(longitude - 9.40416) < 0.00001 assert abs(longitude - 9.40416) < 0.00001
@ -40,6 +39,15 @@ class Test_locator_to_latlong():
assert abs(latitude - 39.222916) < 0.00001 assert abs(latitude - 39.222916) < 0.00001
assert abs(longitude + 86.45416) < 0.00001 assert abs(longitude + 86.45416) < 0.00001
def test_locator_to_latlong_10chars_precision(self):
latitude, longitude = locator_to_latlong("JO81LC39AX")
assert abs(latitude - 51.124913) < 0.000001
assert abs(longitude - 16.941840) < 0.000001
latitude, longitude = locator_to_latlong("MN45GU30AN")
assert abs(latitude - 45.835677) < 0.000001
assert abs(longitude - 68.525173) < 0.000001
def test_locator_to_latlong_consistency_checks_6chars_lower_left_corner(self): def test_locator_to_latlong_consistency_checks_6chars_lower_left_corner(self):
latitude_4, longitude_4 = locator_to_latlong("JN48", center=False) latitude_4, longitude_4 = locator_to_latlong("JN48", center=False)
@ -97,6 +105,9 @@ class Test_locator_to_latlong():
with pytest.raises(ValueError): with pytest.raises(ValueError):
latitude, longitude = locator_to_latlong("JN8QM1") latitude, longitude = locator_to_latlong("JN8QM1")
with pytest.raises(ValueError):
latitude, longitude = locator_to_latlong("JN8QM1AA")
def test_locator_to_latlong_invalid_characters(self): def test_locator_to_latlong_invalid_characters(self):
with pytest.raises(ValueError): with pytest.raises(ValueError):

View file

@ -65,6 +65,7 @@ class Test_utils_freq_to_band():
assert freq_to_band(1200000) == {"band" : 0.23, "mode":None} assert freq_to_band(1200000) == {"band" : 0.23, "mode":None}
def test_shf_frequencies(self): def test_shf_frequencies(self):
assert freq_to_band(2320200) == {"band" : 0.13, "mode":None}
assert freq_to_band(2390000) == {"band" : 0.13, "mode":None} assert freq_to_band(2390000) == {"band" : 0.13, "mode":None}
assert freq_to_band(3300000) == {"band" : 0.09, "mode":None} assert freq_to_band(3300000) == {"band" : 0.09, "mode":None}