Convert between packed and unpacked Minor Planet Center (MPC) designations for asteroids, comets, and natural satellites.
Based on the MPC specification: https://www.minorplanetcenter.net/iau/info/PackedDes.html
Python users: pip install mpc-designation. See docs/QUICKSTART.md for every other language.
| Language | Directory | Status |
|---|---|---|
| AWK | awk/ |
Production |
| Bash | bash/ |
Production |
| C | c/ |
Production |
| C++ | cpp/ |
Production |
| C# | csharp/ |
Production |
| Forth | forth/ |
Production |
| Fortran | fortran/ |
Production |
| Go | go/ |
Production |
| Haskell | haskell/ |
Production |
| Java | java/ |
Production |
| JavaScript | js/ |
Production |
| Julia | julia/ |
Production |
| Kotlin | kotlin/ |
Production |
| Nim | nim/ |
Production |
| Octave/MATLAB | octave/ |
Production |
| Perl | perl/ |
Production |
| PHP | php/ |
Production |
| Python | python/ |
Production |
| R | r/ |
Production |
| Ruby | ruby/ |
Production |
| Rust | rust/ |
Production |
| SPP/IRAF | spp/ |
Production |
| Swift | swift/ |
Production |
| Tcl | tcl/ |
Production |
| TypeScript | typescript/ |
Production |
All production implementations pass the same test suite and produce identical results.
Note: The Forth implementation passes 86 comprehensive tests covering all formats but has not been verified on the full 2M CSV test due to gforth memory constraints. The SPP/IRAF implementation requires the IRAF environment for compilation.
All 25 implementations and their test status on the full test suite (2,022,404 conversions):
| Implementation | Pack (unpacked→packed) | Status |
|---|---|---|
| AWK | 2,022,404 / 2,022,404 | ✅ 100% |
| Bash | 2,022,404 / 2,022,404 | ✅ 100% |
| C | 2,022,404 / 2,022,404 | ✅ 100% |
| C++ | 2,022,404 / 2,022,404 | ✅ 100% |
| C# | 2,022,404 / 2,022,404 | ✅ 100% |
| Forth | 86 / 86 | ✅ 100%* |
| Fortran | 2,022,404 / 2,022,404 | ✅ 100% |
| Go | 2,022,404 / 2,022,404 | ✅ 100% |
| Haskell | 2,022,404 / 2,022,404 | ✅ 100% |
| Java | 2,022,404 / 2,022,404 | ✅ 100% |
| JavaScript | 2,022,404 / 2,022,404 | ✅ 100% |
| Julia | 2,022,404 / 2,022,404 | ✅ 100% |
| Kotlin | 2,022,404 / 2,022,404 | ✅ 100% |
| Nim | 2,022,404 / 2,022,404 | ✅ 100% |
| Octave | 2,022,404 / 2,022,404 | ✅ 100% |
| Perl | 2,022,404 / 2,022,404 | ✅ 100% |
| PHP | 2,022,404 / 2,022,404 | ✅ 100% |
| Python | 2,022,404 / 2,022,404 | ✅ 100% |
| R | 2,022,404 / 2,022,404 | ✅ 100% |
| Ruby | 2,022,404 / 2,022,404 | ✅ 100% |
| Rust | 2,022,404 / 2,022,404 | ✅ 100% |
| SPP/IRAF | 2,022,404 / 2,022,404 | ✅ 100% |
| Swift | 2,022,404 / 2,022,404 | ✅ 100% |
| Tcl | 2,022,404 / 2,022,404 | ✅ 100% |
| TypeScript | 2,022,404 / 2,022,404 | ✅ 100% |
*Forth passes 86 comprehensive tests covering all formats; full CSV test limited by gforth memory constraints.
Roundtrip note: 2,625 old-style designations (e.g., A873 OA) normalize to modern format (1873 OA) on unpack. This is correct behavior—the packed representation is identical either way.
cd awk
echo "1995 XA" | awk -f src/mpc_designation.awk -f src/mpc_designation_main.awk
# Output: J95X00Acd bash
./src/mpc_designation.sh '1995 XA' # Output: J95X00Asource src/mpc_designation.sh
result=$(convert_simple "1995 XA") # Returns "J95X00A"cd c && make
./mpc_designation '1995 XA' # Output: J95X00Acd cpp && make
./mpc_designation '1995 XA' # Output: J95X00A#include "mpc_designation.hpp"
std::string result = mpc::MPCDesignation::convertSimple("1995 XA"); // Returns "J95X00A"cd csharp && dotnet build
dotnet run -- '1995 XA' # Output: J95X00Ausing MPC;
string result = MPCDesignation.ConvertSimple("1995 XA"); // Returns "J95X00A"cd forth
gforth src/mpc_designation_cli.fs '1995 XA' # Output: J95X00Arequire src/mpc_designation.fs
s" 1995 XA" convert-simple type \ Prints: J95X00Afrom mpc_designation import convert_simple
convert_simple('1995 XA') # Returns 'J95X00A'source("mpc_designation.R")
convert_simple("1995 XA") # Returns "J95X00A"cd r
Rscript src/mpc_designation_cli.R '1995 XA' # Output: J95X00Asource mpc_designation.tcl
MPCDesignation::convertSimple "1995 XA" ;# Returns "J95X00A"cd swift && make
./mpc_designation '1995 XA' # Output: J95X00Ause MPC::Designation qw(convert_simple);
convert_simple('1995 XA'); # Returns 'J95X00A'require_once 'src/MPCDesignation.php';
use MPC\MPCDesignation;
MPCDesignation::convertSimple('1995 XA'); // Returns 'J95X00A'cd php
php src/mpc_designation_cli.php '1995 XA' # Output: J95X00Acd fortran && make
./build/mpc_designation_cli '1995 XA' # Output: J95X00Ause mpc_designation
character(len=80) :: result
result = convert_simple('1995 XA') ! Returns 'J95X00A'cd go && make
./mpc_designation '1995 XA' # Output: J95X00Aimport "github.com/rlseaman/mpc_designations/go/mpc"
result, _ := mpc.ConvertSimple("1995 XA") // Returns "J95X00A"cd haskell && make
./build/mpc_designation '1995 XA' # Output: J95X00Aimport MPCDesignation
case convertSimple "1995 XA" of
Right result -> putStrLn result -- "J95X00A"
Left err -> print errcd rust && cargo build --release
./target/release/mpc_designation '1995 XA' # Output: J95X00Ause mpc_designation::convert_simple;
let result = convert_simple("1995 XA").unwrap(); // Returns "J95X00A"cd java && make
java -cp classes mpc.MPCDesignationCLI '1995 XA' # Output: J95X00Aimport mpc.MPCDesignation;
String result = MPCDesignation.convertSimple("1995 XA"); // Returns "J95X00A"cd julia
julia src/mpc_designation_cli.jl '1995 XA' # Output: J95X00Apush!(LOAD_PATH, "src")
using MPCDesignation
result = convert_simple("1995 XA") # Returns "J95X00A"cd kotlin && make
kotlin -cp build/mpc_designation.jar mpc.MainKt '1995 XA' # Output: J95X00Aimport mpc.MPCDesignation
val result = MPCDesignation.convertSimple("1995 XA") // Returns "J95X00A"cd nim && make
./mpc_designation '1995 XA' # Output: J95X00Aimport mpc_designation
let result = convertSimple("1995 XA") # Returns "J95X00A"source('src/mpc_designation.m');
mpc_convert_simple('1995 XA') % Returns 'J95X00A'cd octave
octave --no-gui src/mpc_designation_cli.m '1995 XA' # Output: J95X00Arequire_relative 'src/mpc_designation'
MPCDesignation.convert_simple('1995 XA') # Returns 'J95X00A'cd ruby
ruby src/mpc_designation_cli.rb '1995 XA' # Output: J95X00Ainclude "mpc_designation.x"
char input[80], output[80]
call strcpy ("1995 XA", input, 80)
call mpc_convert (input, output, 80) # output = "J95X00A"
cd spp/build
xc example_usage.x mpc_designation.x -o mpcdes.e
./mpcdes.e mpcdes # Interactive converterconst { convertSimple } = require('./src/mpc_designation');
convertSimple('1995 XA'); // Returns 'J95X00A'cd js
node src/mpc_designation_cli.js '1995 XA' # Output: J95X00Aimport { convertSimple } from 'mpc-designation-ts';
const result: string = convertSimple('1995 XA'); // Returns 'J95X00A'cd typescript && npm install && npm run build
node dist/src/mpc_designation_cli.js '1995 XA' # Output: J95X00A# Asteroids - permanent (numbered)
1 -> 00001
100001 -> A0001
620000 -> ~0000
# Asteroids - provisional
1995 XA -> J95X00A
2024 AB631 -> _OA004S
# Asteroids - survey
2040 P-L -> PLS2040
# Comets
1P -> 0001P
C/1995 O1 -> CJ95O010
D/1993 F2-B -> DJ93F02b
# Natural satellites
S/2019 S 22 -> SK19S220MPC_designations/
├── README.md # This file
├── VERSION # Version number (unified across languages)
├── LICENSE # Public domain
├── CONTRIBUTING.md # How to add new languages
├── docs/
│ ├── SPECIFICATION.md # MPC format reference
│ ├── FORMATS.md # Quick reference tables
│ └── ERROR_CHECKING.md # Validation documentation
├── test-data/
│ ├── prov_unpack_to_pack.csv.gz # 2M+ test cases
│ └── error_test_cases.csv # Error handling tests
├── awk/
│ ├── README.md # AWK documentation
│ ├── Makefile
│ ├── src/ # Source code
│ └── test/ # Test files
├── bash/
│ ├── README.md # Bash documentation
│ ├── Makefile
│ ├── src/ # Source code
│ └── test/ # Test files
├── c/
│ ├── README.md # C documentation
│ ├── Makefile
│ ├── src/ # Source code
│ ├── test/ # Test files
│ └── examples/ # Usage examples
├── cpp/
│ ├── README.md # C++ documentation
│ ├── Makefile
│ └── src/ # Source code
├── csharp/
│ ├── README.md # C# documentation
│ ├── Makefile
│ └── src/ # Source code
├── forth/
│ ├── README.md # Forth documentation
│ ├── Makefile
│ ├── src/ # Source code (gforth)
│ └── test/ # Test files
├── fortran/
│ ├── README.md # Fortran documentation
│ ├── Makefile
│ ├── src/ # Source code
│ └── test/ # Test files
├── python/
│ ├── README.md # Python documentation
│ ├── pyproject.toml
│ ├── src/ # Source code
│ ├── test/ # Test files
│ └── examples/ # Usage examples
├── r/
│ ├── README.md # R documentation
│ ├── Makefile
│ ├── src/ # Source code
│ ├── test/ # Test files
│ └── examples/ # Usage examples
├── tcl/
│ ├── README.md # Tcl documentation
│ ├── src/ # Source code
│ ├── test/ # Test files
│ └── examples/ # Usage examples
├── swift/
│ ├── Makefile
│ ├── src/ # Source code
│ └── test/ # Test files
├── perl/
│ ├── src/ # Source code (MPC/Designation.pm)
│ └── test/ # Test files
├── php/
│ ├── Makefile
│ └── src/ # Source code
├── go/
│ ├── go.mod # Go module definition
│ ├── Makefile
│ ├── mpc/ # Library package
│ ├── cmd/ # CLI
│ └── test/ # Test runners
├── haskell/
│ ├── README.md # Haskell documentation
│ ├── Makefile
│ ├── src/ # Source code (MPCDesignation module)
│ └── test/ # Test files
├── java/
│ ├── Makefile
│ ├── src/ # Source code (mpc package)
│ └── test/ # Test files
├── julia/
│ ├── Makefile
│ ├── src/ # Source code (MPCDesignation module)
│ └── test/ # Test files
├── kotlin/
│ ├── Makefile
│ ├── src/ # Source code (mpc package)
│ └── test/ # Test files
├── nim/
│ ├── Makefile
│ ├── src/ # Source code
│ └── test/ # Test files
├── octave/
│ ├── README.md # Octave/MATLAB documentation
│ ├── Makefile
│ ├── src/ # Source code
│ └── test/ # Test files
├── rust/
│ ├── Cargo.toml # Rust package definition
│ ├── Makefile
│ └── src/ # Source code (lib.rs + binaries)
├── spp/
│ ├── README.md # SPP/IRAF documentation
│ ├── ARCHITECTURE.txt # Code organization
│ ├── src/ # Source code (mpc_designation.x)
│ └── test/ # Test files
├── ruby/
│ ├── Makefile
│ └── src/ # Source code
├── js/
│ ├── package.json # npm package definition
│ ├── Makefile
│ ├── src/ # Source code
│ └── test/ # Test files
└── typescript/
├── package.json # npm package definition
├── tsconfig.json # TypeScript configuration
├── Makefile
├── src/ # Source code
└── test/ # Test files
Each implementation includes tests against 2+ million known-good conversions and 94 error handling tests.
# Run all tests for all languages
make test-all
# Run tests for a specific language
make test-awk
make test-bash
make test-c
make test-cpp
make test-csharp
make test-forth
make test-fortran
make test-go
make test-haskell
make test-java
make test-julia
make test-js
make test-kotlin
make test-nim
make test-octave
make test-perl
make test-php
make test-python
make test-r
make test-ruby
make test-rust
make test-spp
make test-swift
make test-tcl
make test-typescript
# Quick error tests only (faster)
make test-errors
# Benchmark all implementations
./scripts/benchmark.shDownload only the language you need:
git clone --filter=blob:none --sparse https://github.com/rlseaman/MPC_designations.git
cd MPC_designations
git sparse-checkout set python test-data docs| Type | Unpacked | Packed |
|---|---|---|
| Permanent (< 100K) | 1 - 99999 | 00001 - 99999 |
| Permanent (100K-620K) | 100001 - 619999 | A0001 - z9999 |
| Permanent (620K+) | 620000+ | ~0000+ |
| Provisional | 1995 XA | J95X00A |
| Survey | 2040 P-L | PLS2040 |
| Type | Unpacked | Packed |
|---|---|---|
| Numbered | 1P | 0001P |
| Numbered with fragment | 73P-A | 0073Pa |
| Numbered with 2-letter fragment | 73P-AA | 0073Paa |
| Provisional | C/1995 O1 | CJ95O010 |
| Provisional with fragment | D/1993 F2-B | DJ93F02b |
| Ancient | C/240 V1 | C240V010 |
| BCE | C/-146 P1 | C.53P010 |
| Unpacked | Packed |
|---|---|
| S/2019 S 22 | SK19S220 |
The following helper functions are available in C, Go, Java, JavaScript, Perl, PHP, Python, Ruby, Rust, Tcl, and TypeScript implementations:
| Function | Description |
|---|---|
toReportFormat |
Convert minimal packed format to 12-character MPC observation report format |
fromReportFormat |
Convert 12-character report format to minimal packed format |
hasFragment |
Check if a comet designation has a fragment suffix |
getFragment |
Extract fragment suffix (uppercase) from a comet designation |
getParent |
Get parent comet designation without fragment |
designationsEqual |
Compare two designations across different formats |
# Python
to_report_format('0073Pa') # '0073P a' (numbered comet with fragment)
to_report_format('00001') # ' 00001' (numbered asteroid)
from_report_format('0073P a') # '0073Pa'# Python
has_fragment('73P-A') # True
has_fragment('73P') # False
get_fragment('73P-A') # 'A'
get_fragment('73P-AA') # 'AA'
get_parent('73P-A') # '73P'
get_parent('0073Pa') # '0073P'# Python - same object, different formats
designations_equal('1995 XA', 'J95X00A') # True
designations_equal('73P-A', '0073Pa') # True
designations_equal('73P-A', '73P-B') # FalseBenchmark results on Apple M1 Max, processing 2,022,404 designation conversions.
| Rank | Language | Time (ms) | Rate (entries/sec) | Relative |
|---|---|---|---|---|
| 1 | Go | 566 | 3,569,824 | 1.00x |
| 2 | Nim | 569 | 3,550,257 | 0.99x |
| 3 | JavaScript | 834 | 2,423,369 | 0.68x |
| 4 | C | 970 | 2,083,598 | 0.58x |
| 5 | SPP/IRAF | 1,000 | 2,022,404 | 0.57x |
| 6 | TypeScript | 1,163 | 1,737,825 | 0.49x |
| 7 | Rust | 1,317 | 1,534,617 | 0.43x |
| 8 | Fortran | 2,524 | 800,749 | 0.22x |
| 9 | Julia | 3,051 | 662,503 | 0.19x |
| 10 | C# | 3,265 | 619,017 | 0.17x |
| 11 | Forth* | ~4,000 | ~500,000 | 0.14x |
| 12 | Kotlin | 5,822 | 347,147 | 0.10x |
| 13 | PHP | 6,348 | 318,365 | 0.09x |
| 14 | Haskell | 7,542 | 267,971 | 0.08x |
| 15 | Swift | 8,871 | 227,831 | 0.06x |
| 16 | AWK | 9,360 | 215,936 | 0.06x |
| 17 | Python | 10,201 | 198,133 | 0.06x |
| 18 | Java | 10,504 | 192,412 | 0.05x |
| 19 | Perl | 15,965 | 126,595 | 0.04x |
| 20 | C++ | 22,068 | 91,585 | 0.03x |
| 21 | Ruby | 22,296 | 90,648 | 0.03x |
| 22 | Tcl | 33,971 | 59,495 | 0.02x |
| 23 | R | 603,586 | 3,348 | <0.01x |
| 24 | Octave | 768,240 | 2,631 | <0.01x |
| 25 | Bash | ~6,000,000 | ~340 | <0.01x |
*Forth time estimated from 10k sample; Bash time extrapolated (full run would take ~100 minutes)
| Rank | Language | Time (ms) | Rate (entries/sec) | Relative |
|---|---|---|---|---|
| 1 | Nim | 266 | 7,576,833 | 1.00x |
| 2 | C | 284 | 7,116,514 | 0.94x |
| 3 | C# | 297 | 6,805,017 | 0.90x |
| 4 | JavaScript | 454 | 4,451,740 | 0.59x |
| 5 | C++ | 456 | 4,432,215 | 0.59x |
| 6 | Go | 469 | 4,308,162 | 0.57x |
| 7 | TypeScript | 576 | 3,508,837 | 0.46x |
| 8 | Rust | 796 | 2,539,058 | 0.34x |
| 9 | SPP/IRAF | ~1,000 | ~2,022,404 | 0.27x |
| 10 | Haskell | 1,179 | 1,714,239 | 0.23x |
| 11 | Kotlin | 1,333 | 1,516,196 | 0.20x |
| 12 | Fortran | 1,939 | 1,042,336 | 0.14x |
| 13 | Java | 2,293 | 881,417 | 0.12x |
| 14 | Julia | 2,650 | 762,610 | 0.10x |
| 15 | Forth* | ~4,000 | ~500,000 | 0.07x |
| 16 | PHP | 4,323 | 467,488 | 0.06x |
| 17 | Swift | 4,363 | 463,234 | 0.06x |
| 18 | Python | 4,929 | 410,014 | 0.05x |
| 19 | AWK | 9,254 | 218,402 | 0.03x |
| 20 | Ruby | 11,813 | 171,090 | 0.02x |
| 21 | Perl | 12,426 | 162,650 | 0.02x |
| 22 | Tcl | 18,567 | 108,854 | 0.01x |
| 23 | R* | ~400,000 | ~5,000 | <0.01x |
| 24 | Octave* | ~500,000 | ~4,000 | <0.01x |
| 25 | Bash | ~7,200,000 | ~280 | <0.01x |
*Forth time estimated from 10k sample; R and Octave times estimated (too slow to benchmark); Bash time extrapolated; SPP time estimated (requires IRAF environment)
All production implementations pass the packed round-trip test: pack(unpack(y)) = y.
The 2,625 old-style provisional designations (e.g., A873 OA for year 1873) normalize to modern format (1873 OA) on unpack. This is expected behavior—both formats pack to the same representation (I73O00A).
Run benchmarks with: ./scripts/benchmark.sh --roundtrip
- Specification - MPC format specification reference
- Format Tables - Quick reference for all formats
- Error Checking - Validation documentation
- Multi-Platform - Platform support and compatibility
See CONTRIBUTING.md for guidelines on adding new language implementations.
CC0 1.0 Universal - Public Domain Dedication. See LICENSE.