Skip to content

Commit e8f5699

Browse files
committed
squashed commits for names.py
removed second instance of pkd_noI definition fixed test Update test_names.py Update test_names.py changed indentation of a closing ) fixed error with unpacking permanent desigs fixed typo modified code structure slightly + added tests removed test_break_packed() case that is no longer invalid fixed typo in test to_packed() statements added quotes to numerical designations in test statements fixed typos added functionality for extended permanent asteroid designations continuing to try to cover code with tests Update test_names.py Update test_names.py added another test error added different test errors Update test_names.py Update test_names.py error not being raised...testing more testing error raising removed example that didn't raise error changed ValueError and IndexError to TargetNameParseError changed some ValueError examples to IndexError added ValueError examples to test_names.py added ValueError examples to test_names.py fixed typo fixed typo added more data validation added more data validation checks and fixed some typos Update names.py Added check for value errors in to_packed Added check for value errors in extended provisional designation code block of to_packed() missing quotes in example results added missing quotes in example results fixed another bug in to_packed() fixed another bug in to_packed() where designations with 2-digit subscripts were getting skipped fixed error where some packed desigs were failing return statement was missing in to_packed() for provisional designations with no subscript number or subscripts of 1 various changes - added examples of extended provisional designation handling to names.rst - fixed bug in to_packed() - added tests to test_names.py forgot math import command forgot math import command Added/removed white spaces Added/removed white spaces Added extended packed desig functionality Added functionality for extended packed provisional designations to be implemented by the MPC in anticipation of higher discovery rates from LSST (https://minorplanetcenter.net/mpcops/documentation/provisional-designation-definition/)
1 parent df7f292 commit e8f5699

File tree

4 files changed

+196
-42
lines changed

4 files changed

+196
-42
lines changed

CHANGES.rst

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
1+
0.6.0 (unreleased)
2+
==================
3+
4+
New Features
5+
------------
6+
7+
sbpy.names
8+
^^^^^^^^^^
9+
- Added functionality to `sbpy.Names.from_packed()` and
10+
`sbpy.Names.to_packed()` to handle new extended provisional designations
11+
to be implemented by the MPC in anticipation of higher asteroid discovery
12+
rates in the LSST survey era [#406]
13+
14+
115
0.5.0 (2024-08-28)
216
==================
317

docs/sbpy/data/names.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,13 @@ numbers and unpacked ones:
112112

113113
>>> Names.from_packed('J95A01A')
114114
'1995 AA1'
115+
>>> Names.from_packed('_RD0aEM')
116+
'2027 DZ6190'
115117
>>> Names.from_packed('G3693')
116118
163693
117119
>>> Names.to_packed('1995 AA1')
118120
'J95A01A'
121+
>>> Names.to_packed('2027 DZ6190')
122+
'_RD0aEM'
119123
>>> Names.to_packed('163693')
120124
'G3693'

sbpy/data/names.py

Lines changed: 102 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"""
1212

1313
import re
14+
import math
1415
from ..exceptions import SbpyException
1516

1617
__all__ = ['Names', 'TargetNameParseError', 'natural_sort_key']
@@ -68,6 +69,9 @@ class Names():
6869
pkd = ('0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
6970
'abcdefghijklmnopqrstuvwxyz')
7071

72+
# packed numbers translation string with no I
73+
pkd_noI = 'ABCDEFGHJKLMNOPQRSTUVWXYZ'
74+
7175
@staticmethod
7276
def to_packed(s):
7377
"""Convert designation or number to packed identifier.
@@ -94,14 +98,22 @@ def to_packed(s):
9498
s = int(s)
9599
if s < 100000:
96100
return '{:05d}'.format(s)
97-
elif s > 619999:
98-
raise TargetNameParseError(
99-
'{} cannot be turned into a packed number'.format(s)
100-
)
101-
else:
101+
elif (s > 99999 and s < 620000):
102102
mod = (s % 10000)
103103
return '{}{:04d}'.format(
104104
Names.pkd[int((s - mod) / 10000)], mod)
105+
elif (s > 619999 and s < 15396336):
106+
s = s - 620000
107+
d = ['0', '0', '0', '0']
108+
for idx in reversed(range(0, 4)):
109+
d[idx] = Names.pkd[math.floor(s % 62)]
110+
s //= 62
111+
return ('~'+''.join(d))
112+
else:
113+
raise TargetNameParseError(
114+
'{} cannot be turned into a packed number'.format(s)
115+
)
116+
105117
elif s.endswith('P-L'):
106118
return 'PLS{}'.format(s[:4])
107119
elif s[-3:] in ['T-1', 'T-2', 'T-3']:
@@ -123,42 +135,61 @@ def to_packed(s):
123135
frag = '0'
124136
num = s[6:]
125137

126-
if num == '':
127-
num = '00'
128-
elif len(num) == 1:
129-
num = '0' + num
130-
elif len(num) > 2:
131-
try:
138+
try:
139+
if num == '':
140+
num = '00'
141+
elif len(num) == 1:
142+
num = '0' + num
143+
elif len(num) > 2:
132144
num = Names.pkd[int(num[:-1])]+num[-1]
133-
except (IndexError, ValueError):
134-
raise TargetNameParseError(
135-
('{} cannot be turned into a '
136-
'packed designation').format(s))
137-
return '{}{}{}{}{}'.format(
138-
Names.pkd[int(float(s[:2]))],
139-
s[2:4],
140-
s[5],
141-
num,
142-
frag.lower()
143-
)
145+
return '{}{}{}{}{}'.format(
146+
Names.pkd[int(float(s[:2]))],
147+
s[2:4],
148+
s[5],
149+
num,
150+
frag.lower()
151+
)
152+
except (IndexError, ValueError):
153+
raise TargetNameParseError(
154+
('{} cannot be turned into a '
155+
'packed designation').format(s))
144156
else:
145-
yr = s.strip()[:4]
146-
yr = Names.pkd[int(yr[:2])] + yr[2:]
147-
let = s.strip()[4:7].strip()
148-
num = s.strip()[7:].strip()
149-
if num == '':
150-
num = '00'
151-
elif len(num) == 1:
152-
num = '0' + num
153-
elif len(num) > 2:
154-
try:
155-
num = Names.pkd[int(num[:-1])]+num[-1]
156-
except (IndexError, ValueError):
157-
raise TargetNameParseError(
158-
('{} cannot be turned into a '
159-
'packed designation').format(s))
160-
return (yr + let[0] + num + let[1])
161-
157+
try:
158+
yr = s.strip()[:4]
159+
yr = Names.pkd[int(yr[:2])] + yr[2:]
160+
let = s.strip()[4:7].strip()
161+
num = s.strip()[7:].strip()
162+
163+
if num == '':
164+
return (yr + let[0] + '00' + let[1])
165+
elif len(num) == 1:
166+
return (yr + let[0] + '0' + num + let[1])
167+
elif len(num) > 1:
168+
obj_num = int(num)*25 + Names.pkd_noI.find(let[1]) + 1
169+
# use original packed desigs for first 15500 objs per month
170+
if obj_num < 15501:
171+
num = Names.pkd[int(num[:-1])]+num[-1]
172+
return (yr + let[0] + num + let[1])
173+
# use extended packed desigs for >15500 objs per month
174+
elif obj_num < 14791837:
175+
obj_num = obj_num - 15501
176+
year = Names.pkd[int(yr[1:])]
177+
month = let[0]
178+
d = ['0', '0', '0', '0']
179+
for idx in reversed(range(0, 4)):
180+
d[idx] = Names.pkd[math.floor(obj_num % 62)]
181+
obj_num //= 62
182+
return ('_'+Names.pkd[int(yr[1:])]+let[0]+''.join(d))
183+
# if more than maximum of 14,791,836 objects per half-month
184+
# accommodated by the extended provisional designation scheme
185+
else:
186+
raise TargetNameParseError(
187+
('{} cannot be turned into a '
188+
'packed number or designation').format(s))
189+
except (IndexError, ValueError):
190+
raise TargetNameParseError(
191+
('{} cannot be turned into a '
192+
'packed number or designation').format(s))
162193
else:
163194
raise TargetNameParseError(
164195
('{} cannot be turned into a '
@@ -189,6 +220,17 @@ def from_packed(p):
189220
return int(p)
190221
elif p[0].isalpha() and p[1:].isdigit():
191222
return int(str(Names.pkd.find(p[0])) + p[1:])
223+
elif p[0] == '~' and p[1:].isalnum():
224+
if len(p) == 5:
225+
obj_num = 620000 + Names.pkd.find(p[1])*(62**3) \
226+
+ Names.pkd.find(p[2])*(62**2) \
227+
+ Names.pkd.find(p[3])*(62) \
228+
+ Names.pkd.find(p[4])
229+
return int(obj_num)
230+
else:
231+
raise TargetNameParseError(
232+
('{} cannot be turned into an '
233+
'unpacked designation').format(p))
192234

193235
# old designation style, e.g.: 1989AB
194236
if (len(p.strip()) < 7 and p[:4].isdigit() and p[4:6].isalpha()):
@@ -216,6 +258,27 @@ def from_packed(p):
216258
(str(Names.pkd.find(p[4])) + p[5]).lstrip('0'),
217259
'-{}'.format(p[6].upper()) if p[6].islower() else ''
218260
)
261+
# MPC extended packed provisional designation
262+
elif p[0] == '_':
263+
if (
264+
(p[1].isalpha() and p[1].isupper())
265+
and re.search("[A-H,J-Y]", p[2])
266+
and p[3:].isalnum()
267+
and len(p) == 7
268+
):
269+
obj_num = 15501 + Names.pkd.find(p[3])*(62**3) \
270+
+ Names.pkd.find(p[4])*(62**2) \
271+
+ Names.pkd.find(p[5])*(62) \
272+
+ Names.pkd.find(p[6])
273+
return '20{} {}{}{}'.format(
274+
str(Names.pkd.find(p[1])),
275+
p[2],
276+
Names.pkd_noI[((obj_num-1) % 25)],
277+
math.floor((obj_num-1)/25))
278+
else:
279+
raise TargetNameParseError(
280+
('{} cannot be turned into an '
281+
'unpacked designation').format(p))
219282
else:
220283
# nothing to do
221284
return p

sbpy/data/tests/test_names.py

Lines changed: 76 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,12 @@ def test_from_packed():
102102
103103
Test values from https://www.minorplanetcenter.net/iau/info/PackedDes.html
104104
105+
Test values for extended permanent designations from
106+
https://www.minorplanetcenter.net/iau/info/PackedDes.html
107+
108+
Test values for extended provisional designations from
109+
https://minorplanetcenter.net/mpcops/documentation/provisional-designation-definition/
110+
105111
"""
106112

107113
# minor planets
@@ -129,6 +135,22 @@ def test_from_packed():
129135
assert Names.from_packed('K33L89c') == '2033 L89-C'
130136
assert Names.from_packed('K88AA30') == '2088 A103'
131137

138+
# extended permanent designations
139+
assert Names.from_packed('~0000') == 620000
140+
assert Names.from_packed('~000z') == 620061
141+
assert Names.from_packed('~AZaz') == 3140113
142+
assert Names.from_packed('~zzzz') == 15396335
143+
144+
# extended provisional designations
145+
assert Names.from_packed('_QC0000') == '2026 CA620'
146+
assert Names.from_packed('_QC0aEM') == '2026 CZ6190'
147+
assert Names.from_packed('_QCzzzz') == '2026 CL591673'
148+
assert Names.from_packed('_PD0000') == '2025 DA620'
149+
assert Names.from_packed('_QD000N') == '2026 DY620'
150+
assert Names.from_packed('_RD0aEM') == '2027 DZ6190'
151+
assert Names.from_packed('_SEZZZZ') == '2028 EA339749'
152+
assert Names.from_packed('_TFzzzz') == '2029 FL591673'
153+
132154
# a few other tests
133155
assert Names.from_packed('50000') == 50000
134156
assert Names.from_packed('A0345') == 100345
@@ -142,6 +164,12 @@ def test_to_packed():
142164
143165
Test values from https://www.minorplanetcenter.net/iau/info/PackedDes.html
144166
167+
Test values for extended permanent designations from
168+
https://www.minorplanetcenter.net/iau/info/PackedDes.html
169+
170+
Test values for extended provisional designations from
171+
https://minorplanetcenter.net/mpcops/documentation/provisional-designation-definition/
172+
145173
"""
146174

147175
# minor planets
@@ -169,6 +197,22 @@ def test_to_packed():
169197
assert Names.to_packed('2033 L89-C') == 'K33L89c'
170198
assert Names.to_packed('2088 A103') == 'K88AA30'
171199

200+
# extended permanent designations
201+
assert Names.to_packed('620000') == '~0000'
202+
assert Names.to_packed('620061') == '~000z'
203+
assert Names.to_packed('3140113') == '~AZaz'
204+
assert Names.to_packed('15396335') == '~zzzz'
205+
206+
# extended provisional designations
207+
assert Names.to_packed('2026 CA620') == '_QC0000'
208+
assert Names.to_packed('2026 CZ6190') == '_QC0aEM'
209+
assert Names.to_packed('2026 CL591673') == '_QCzzzz'
210+
assert Names.to_packed('2025 DA620') == '_PD0000'
211+
assert Names.to_packed('2026 DY620') == '_QD000N'
212+
assert Names.to_packed('2027 DZ6190') == '_RD0aEM'
213+
assert Names.to_packed('2028 EA339749') == '_SEZZZZ'
214+
assert Names.to_packed('2029 FL591673') == '_TFzzzz'
215+
172216
# a few other tests
173217
assert Names.to_packed('50000') == '50000'
174218
assert Names.to_packed('100345') == 'A0345'
@@ -223,11 +267,40 @@ def test_parse_asteroid():
223267

224268

225269
def test_break_packed():
226-
with pytest.raises(TargetNameParseError):
227-
Names.to_packed('620000')
228-
229270
with pytest.raises(TargetNameParseError):
230271
Names.to_packed('2015 this will not work')
231272

232273
with pytest.raises(TargetNameParseError):
233274
Names.to_packed('thiswillnotwork')
275+
276+
277+
def test_raises_error():
278+
with pytest.raises(TargetNameParseError):
279+
Names.to_packed('2011 AA123456789')
280+
281+
with pytest.raises(TargetNameParseError):
282+
Names.to_packed('2011 A123456789')
283+
284+
with pytest.raises(TargetNameParseError):
285+
Names.to_packed('2026 CL591674')
286+
287+
with pytest.raises(TargetNameParseError):
288+
Names.to_packed('1989 A')
289+
290+
with pytest.raises(TargetNameParseError):
291+
Names.to_packed('15396336')
292+
293+
with pytest.raises(TargetNameParseError):
294+
Names.from_packed('~555555')
295+
296+
with pytest.raises(TargetNameParseError):
297+
Names.from_packed('_QCzzzz0')
298+
299+
with pytest.raises(TargetNameParseError):
300+
Names.from_packed('_Qczzzz')
301+
302+
with pytest.raises(TargetNameParseError):
303+
Names.from_packed('_QCzz_z')
304+
305+
with pytest.raises(TargetNameParseError):
306+
Names.from_packed('_qCzzzz')

0 commit comments

Comments
 (0)