attitude adjustment
This commit is contained in:
parent
e308353d9b
commit
ad630a9428
|
@ -0,0 +1,77 @@
|
||||||
|
# Attitude Adjustment
|
||||||
|
|
||||||
|
**Category**: Astronomy, Astrophysics, Astrometry, Astrodynamics, AAAA
|
||||||
|
**Points (final)**: 69 points
|
||||||
|
**Solves**: 62
|
||||||
|
|
||||||
|
> Our star tracker has collected a set of boresight reference vectors, and identified which stars in the catalog they correspond to. Compare the included catalog and the identified boresight vectors to determine what our current attitude is.
|
||||||
|
>
|
||||||
|
> Note: The catalog format is unit vector (X,Y,Z) in a celestial reference frame and the magnitude (relative brightness)
|
||||||
|
|
||||||
|
|
||||||
|
## Writeup
|
||||||
|
by [erin (`barzamin`)](https://imer.in).
|
||||||
|
|
||||||
|
For this problem, we have two sets of N vectors which are paired; all points in the first set are just those in the second set up to rotation. Since we already know which point in the observation set maps to which vector in the catalog set, we can use the [Kabsch algorithm](https://en.wikipedia.org/wiki/Kabsch_algorithm) to find the rotation matrix (note that this is called an *orthogonal Procrustes problem*).
|
||||||
|
|
||||||
|
Since nobody has time to implement computational geometry during a ctf, I grabbed an existing Kabsch implementation. For some reason, I didn't notice that `scipy.spatial` has [a Kabsch implementation](https://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.transform.Rotation.align_vectors.html) built in, so I used some random external project, [`rmsd`](https://github.com/charnley/rmsd).
|
||||||
|
|
||||||
|
First, load the star catalog:
|
||||||
|
```{.python}
|
||||||
|
catalog = {}
|
||||||
|
with open('./attitude-papa21503yankee/test.txt') as f:
|
||||||
|
i = 0
|
||||||
|
for line in f:
|
||||||
|
[x, y, z, m] = [float(s.strip()) for s in line.split(',')]
|
||||||
|
catalog[i] = {'v': np.array([x,y,z]), 'm':m}
|
||||||
|
i += 1
|
||||||
|
```
|
||||||
|
|
||||||
|
Set up some helpers for parsing the output of the challenge server and solving an orientation:
|
||||||
|
```{.python}
|
||||||
|
def parse_stars(stardata):
|
||||||
|
stars = {}
|
||||||
|
for line in stardata.strip().split('\n'):
|
||||||
|
line = line.strip()
|
||||||
|
star_id = int(line.split(':')[0].strip())
|
||||||
|
direction = np.array([float(x) for x in line.split(':')[1].split(',\t')])
|
||||||
|
stars[star_id] = direction
|
||||||
|
return stars
|
||||||
|
|
||||||
|
def solve_orientation(stars, catalog):
|
||||||
|
P = np.vstack(list(stars.values()))
|
||||||
|
Q = np.vstack([catalog[i]['v'] for i in stars.keys()])
|
||||||
|
print("rmsd: {}".format(calculate_rmsd.kabsch_rmsd(P,Q)))
|
||||||
|
rotation_mtx = calculate_rmsd.kabsch(P, Q)
|
||||||
|
rotation = Rotation.from_matrix(np.linalg.inv(rotation_mtx))
|
||||||
|
return rotation
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that I threw in an inversion of the rotation matrix; this is because I should've been aligning from the catalog *to* the current star locations. Switching P to be the catalog and Q to be stars would've done the same thing.
|
||||||
|
|
||||||
|
Then we just grabbed each challenge from the computer, aligned the sets, and spat the orientation of the satellite back at the server to get
|
||||||
|
|
||||||
|
```{.python}
|
||||||
|
TICKET = 'ticket{papa21503yankee:GGntHycE-_FqMqGbIoz7rKZD-MzEraoec3dRGU21ExVYGGYFLJQQlRLqNLWn8D4ghQ}'
|
||||||
|
r = tubes.remote.remote('attitude.satellitesabove.me', 5012)
|
||||||
|
r.send(TICKET+'\n')
|
||||||
|
time.sleep(0.5)
|
||||||
|
for _ in range(20):
|
||||||
|
r.recvuntil(b'--------------------------------------------------\n', drop=True)
|
||||||
|
stars = parse_stars(r.recv().decode())
|
||||||
|
rotation = solve_orientation(stars, catalog)
|
||||||
|
r.send(','.join([str(x) for x in rotation.as_quat()]) + '\n')
|
||||||
|
time.sleep(0.1)
|
||||||
|
print(r.clean())
|
||||||
|
```
|
||||||
|
|
||||||
|
The flag should get printed out on stdout by the final line.
|
||||||
|
|
||||||
|
### Full code
|
||||||
|
```{.python include=attitude.py}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Resources and other writeups
|
||||||
|
- https://en.wikipedia.org/wiki/Orthogonal_Procrustes_problem
|
||||||
|
- https://en.wikipedia.org/wiki/Kabsch_algorithm
|
||||||
|
- https://github.com/charnley/rmsd/tree/master
|
|
@ -0,0 +1,43 @@
|
||||||
|
import numpy as np
|
||||||
|
from pwnlib import tubes
|
||||||
|
import time
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
from rmsd import calculate_rmsd
|
||||||
|
from scipy.spatial.transform import Rotation
|
||||||
|
|
||||||
|
catalog = {}
|
||||||
|
with open('./attitude-papa21503yankee/test.txt') as f:
|
||||||
|
i = 0
|
||||||
|
for line in f:
|
||||||
|
[x, y, z, m] = [float(s.strip()) for s in line.split(',')]
|
||||||
|
catalog[i] = {'v': np.array([x,y,z]), 'm':m}
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
def parse_stars(stardata):
|
||||||
|
stars = {}
|
||||||
|
for line in stardata.strip().split('\n'):
|
||||||
|
line = line.strip()
|
||||||
|
star_id = int(line.split(':')[0].strip())
|
||||||
|
direction = np.array([float(x) for x in line.split(':')[1].split(',\t')])
|
||||||
|
stars[star_id] = direction
|
||||||
|
return stars
|
||||||
|
|
||||||
|
def solve_orientation(stars, catalog):
|
||||||
|
P = np.vstack(list(stars.values()))
|
||||||
|
Q = np.vstack([catalog[i]['v'] for i in stars.keys()])
|
||||||
|
print("rmsd: {}".format(calculate_rmsd.kabsch_rmsd(P,Q)))
|
||||||
|
rotation_mtx = calculate_rmsd.kabsch(P, Q)
|
||||||
|
rotation = Rotation.from_matrix(np.linalg.inv(rotation_mtx))
|
||||||
|
return rotation
|
||||||
|
|
||||||
|
TICKET = 'ticket{papa21503yankee:GGntHycE-_FqMqGbIoz7rKZD-MzEraoec3dRGU21ExVYGGYFLJQQlRLqNLWn8D4ghQ}'
|
||||||
|
r = tubes.remote.remote('attitude.satellitesabove.me', 5012)
|
||||||
|
r.send(TICKET+'\n')
|
||||||
|
time.sleep(0.5)
|
||||||
|
for _ in range(20):
|
||||||
|
r.recvuntil(b'--------------------------------------------------\n', drop=True)
|
||||||
|
stars = parse_stars(r.recv().decode())
|
||||||
|
rotation = solve_orientation(stars, catalog)
|
||||||
|
r.send(','.join([str(x) for x in rotation.as_quat()]) + '\n')
|
||||||
|
time.sleep(0.1)
|
||||||
|
print(r.clean())
|
Loading…
Reference in New Issue