#!/usr/bin/env python3
# Check UAS operator registration number
# Based on:
# Easy Access Rules for Unmanned Aircraft Systems
# Regulations (EU) 2019/947 and (EU) 2019/945
# AMC1 Article 14(6) Registration of UAS operators and 'certified' UAS
# https://www.easa.europa.eu/en/document-library/easy-access-rules/easy-access-rules-unmanned-aircraft-systems-regulations-eu
#
# https://en.wikipedia.org/wiki/Luhn_mod_N_algorithm
# https://en.wikipedia.org/wiki/Luhn_algorithm
__author__ = "Gernot Walzl"
__date__ = "2026-01-03"
import argparse
import sys
chr_to_val = {
'0': 0,
'1': 1,
'2': 2,
'3': 3,
'4': 4,
'5': 5,
'6': 6,
'7': 7,
'8': 8,
'9': 9,
'a': 10,
'b': 11,
'c': 12,
'd': 13,
'e': 14,
'f': 15,
'g': 16,
'h': 17,
'i': 18,
'j': 19,
'k': 20,
'l': 21,
'm': 22,
'n': 23,
'o': 24,
'p': 25,
'q': 26,
'r': 27,
's': 28,
't': 29,
'u': 30,
'v': 31,
'w': 32,
'x': 33,
'y': 34,
'z': 35
}
def luhn_mod_n_checksum(alphanumerics, n=36):
sum_digits = 0
alphanums_reversed = alphanumerics[::-1]
for idx, alphanum in enumerate(alphanums_reversed):
code_points = chr_to_val[alphanum]
if idx % 2 == 0:
code_points *= 2
if code_points >= n:
code_points -= (n-1)
sum_digits += code_points
checksum = (n - (sum_digits % n)) % n
return checksum
def main(uas_reg_num):
# first three (3) alphanumerics (upper-case only) corresponding to
# the ISO 3166 Alpha-3 code of the Member State of registration
# country = uas_reg_num[0:3]
# twelve (12) randomly generated characters that consist of alphanumerics
# (lower-case only)
alphanumerics = uas_reg_num[3:15]
# one (1) character corresponding to the checksum
checksum = uas_reg_num[15]
# three (3) additional alphanumerics (lower-case only) called
# 'secret digits'
secret = uas_reg_num[17:20]
# generate a checksum by applying the Luhn-mod-36 algorithm to the
# fifteen (15) alphanumerics that result from the concatenation,
# in the following order, of:
# (1) the twelve (12) alphanumerics of the UAS operator registration number
# (2) the three (3) randomly generated 'secret digits'
checksum_expected = luhn_mod_n_checksum(alphanumerics + secret)
result = 1
if chr_to_val[checksum] == checksum_expected:
result = 0
print("OK")
else:
print("FAILED")
return result
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument(
'uas_reg_num', type=str,
help="UAS operator registration number (example: FIN87astrdge12k8-xyz)")
args = parser.parse_args()
sys.exit(main(args.uas_reg_num))