LOST 0.0.1
LOST: Open-source Star Tracker
Loading...
Searching...
No Matches
databases.cpp
Go to the documentation of this file.
1#include "databases.hpp"
2
3#include <stdlib.h>
4#include <assert.h>
5#include <math.h>
6#include <vector>
7#include <cstdint>
8#include <algorithm>
9#include <iostream>
10
11#include "attitude-utils.hpp"
12#include "serialize-helpers.hpp"
13#include "star-utils.hpp"
14
15namespace lost {
16
18
20 return (dbFlags & flag) != 0;
21}
22
28
30 return p1.distance < p2.distance;
31}
32
33// just the index part of the kvector, doesn't store the sorted list it refers to. This makes it
34// flexible whether used to store star distance angles, an angle of a triangle, etc.
48// apparently there's no easy way to accept an iterator argument. Hate java all you want, but at
49// least it can do that!
50// https://stackoverflow.com/questions/5054087/declare-a-function-accepting-generic-iterator or
51// rather, the correct way is to use a template and just use the ++ and * operators willy-nilly,
52// which will throw an error if the operators are not implemented.
53
64void SerializeKVectorIndex(SerializeContext *ser, const std::vector<decimal> &values, decimal min, decimal max, long numBins) {
65 std::vector<int32_t> kVector(numBins+1); // We store sums before and after each bin
66 decimal binWidth = (max - min) / numBins;
67
68 // generate the k-vector part
69 // Idea: When we find the first star that's across any bin boundary, we want to update all the newly sealed bins
70 long lastBin = 0; // first bin the last star belonged to
71 for (int32_t i = 0; i < (int32_t)values.size(); i++) {
72 if (i > 0) {
73 assert(values[i] >= values[i-1]);
74 }
75 assert(values[i] >= min);
76 assert(values[i] <= max);
77 long thisBin = (long)ceil((values[i] - min) / binWidth); // first bin we belong to
78 assert(thisBin >= 0);
79 assert(thisBin <= numBins); // thisBin == numBins is acceptable since kvector length == numBins + 1
80 // if thisBin > lastBin, then no more stars can be added to those bins between thisBin and lastBin, so set them.
81 for (long bin = lastBin; bin < thisBin; bin++) {
82 kVector[bin] = i; // our index is the number of pairs with shorter distance
83 }
85 }
86 for (long bin = lastBin; bin <= numBins; bin++) {
87 kVector[bin] = values.size();
88 }
89
90 // verify kvector
92 for (const int32_t &bin : kVector) {
95 }
96
97 // metadata fields
102
103 // kvector index field
104 for (const int32_t &bin : kVector) {
106 }
107}
108
111
116
117 assert(min >= DECIMAL(0.0));
118 assert(max > min);
119 binWidth = (max - min) / numBins;
120
121 bins = DeserializeArray<int32_t>(des, numBins+1);
122}
123
131 if (maxQueryDistance >= max) {
132 maxQueryDistance = max - DECIMAL(0.00001); // TODO: better way to avoid hitting the bottom bin
133 }
134 if (minQueryDistance <= min) {
135 minQueryDistance = min + DECIMAL(0.00001);
136 }
137 if (minQueryDistance > max || maxQueryDistance < min) {
138 *upperIndex = 0;
139 return 0;
140 }
141 long lowerBin = BinFor(minQueryDistance);
142 long upperBin = BinFor(maxQueryDistance);
144 assert(upperBin <= numBins);
145 // bins[lowerBin-1]=number of pairs <= r < query distance, so it is the index of the
146 // first possible item that might be equal to the query distance
147 int lowerIndex = bins[lowerBin-1];
148 if (lowerIndex >= numValues) {
149 // all pairs have distance less than queried. Return value is irrelevant as long as
150 // numReturned=0
151 return 0;
152 }
153 // bins[upperBin]=number of pairs <= r >= query distance
154 *upperIndex = bins[upperBin];
155 return lowerIndex;
156}
157
159long KVectorIndex::BinFor(decimal query) const {
160 long result = (long)ceil((query - min) / binWidth);
161 assert(result >= 0);
162 assert(result <= numBins);
163 return result;
164}
165
175std::vector<KVectorPair> CatalogToPairDistances(const Catalog &catalog, decimal minDistance, decimal maxDistance) {
176 std::vector<KVectorPair> result;
177 for (int16_t i = 0; i < (int16_t)catalog.size(); i++) {
178 for (int16_t k = i+1; k < (int16_t)catalog.size(); k++) {
179
180 KVectorPair pair = { i, k, AngleUnit(catalog[i].spatial, catalog[k].spatial) };
181 assert(isfinite(pair.distance));
182 assert(pair.distance >= 0);
183 assert(pair.distance <= DECIMAL_M_PI);
184
185 if (pair.distance >= minDistance && pair.distance <= maxDistance) {
186 // we'll sort later
187 result.push_back(pair);
188 }
189 }
190 }
191 return result;
192}
193
199 std::vector<int32_t> kVector(numBins+1); // numBins = length, all elements zero
200 std::vector<KVectorPair> pairs = CatalogToPairDistances(catalog, minDistance, maxDistance);
201
202 // sort pairs in increasing order.
203 std::sort(pairs.begin(), pairs.end(), CompareKVectorPairs);
204 std::vector<decimal> distances;
205
206 for (const KVectorPair &pair : pairs) {
207 distances.push_back(pair.distance);
208 }
209
210 // index field
212
213 // bulk pairs field
214 for (const KVectorPair &pair : pairs) {
217 }
218}
219
226
231
239
241
242 long upperIndex = -1;
244 *end = &pairs[upperIndex * 2];
245 return &pairs[lowerIndex * 2];
246}
247
250
251 // Instead of computing the angle for every pair in the database, we pre-compute the /cosines/
252 // of the min and max query distances so that we can compare against dot products directly! As
253 // angle increases, cosine decreases, up to DECIMAL_M_PI (and queries larger than that don't really make
254 // sense anyway)
256
259
262 // now we need to find the first and last index that actually matches the query
263 // step the lower index forward
264 // There's no good reason to be using >= and <= for the comparison against max/min, but the tests fail otherwise (because they use angle, with its acos, instead of forward cos like us). It's an insignificant difference.
266 && catalog[pairs[liberalLowerIndex*2]].spatial * catalog[pairs[liberalLowerIndex*2+1]].spatial >= maxQueryCos
267 )
268 { liberalLowerIndex++; }
269
270 // step the upper index backward
272 // the liberalUpperIndex is past the end of the logically returned range, so we need to subtract 1
273 && catalog[pairs[(liberalUpperIndex-1)*2]].spatial * catalog[pairs[(liberalUpperIndex-1)*2+1]].spatial <= minQueryCos
274 )
275 { liberalUpperIndex--; }
276
277 *end = &pairs[liberalUpperIndex * 2];
278 return &pairs[liberalLowerIndex * 2];
279}
280
283 return index.NumValues();
284}
285
287std::vector<decimal> PairDistanceKVectorDatabase::StarDistances(int16_t star, const Catalog &catalog) const {
288 std::vector<decimal> result;
289 for (int i = 0; i < NumPairs(); i++) {
290 if (pairs[i*2] == star || pairs[i*2+1] == star) {
291 result.push_back(AngleUnit(catalog[pairs[i*2]].spatial, catalog[pairs[i*2+1]].spatial));
292 }
293 }
294 return result;
295}
296
316const unsigned char *MultiDatabase::SubDatabasePointer(int32_t magicValue) const {
318 DeserializeContext *des = &desValue; // just for naming consistency with how we use `des` elsewhere
319
320 assert(magicValue != 0);
321 while (true) {
323 if (curMagicValue == 0) {
324 return nullptr;
325 }
327
328 // Ensure that our database is using the same type as the runtime.
329 #ifdef LOST_FLOAT_MODE
331 std::cerr << "LOST was compiled in float mode. This database was serialized in double mode and is incompatible." << std::endl;
332 exit(1);
333 }
334 #else
336 std::cerr << "LOST was compiled in double mode. This database was serialized in float mode and is incompatible." << std::endl;
337 exit(1);
338 }
339 #endif
340
342 assert(dbLength > 0);
343 DeserializePadding<uint64_t>(des); // align to an 8-byte boundary
345 if (curMagicValue == magicValue) {
347 }
348 }
349 // shouldn't ever make it here. Compiler should remove this assertion as unreachable.
350 assert(false);
351}
352
355 uint32_t flags) {
356 for (const MultiDatabaseEntry &multiDbEntry : dbs) {
361 std::copy(multiDbEntry.bytes.cbegin(), multiDbEntry.bytes.cend(), std::back_inserter(ser->buffer));
362 }
363 SerializePrimitive<int32_t>(ser, 0); // caboose
364}
365
366}
367
368// TODO: after creating the database, print more statistics, such as average number of pairs per
369// star, stars per bin, space distribution between array and index.
A data structure enabling constant-time range queries into fixed numerical data.
Definition databases.hpp:23
KVectorIndex(DeserializeContext *des)
Construct from serialized buffer.
long QueryLiberal(decimal minQueryDistance, decimal maxQueryDistance, long *upperIndex) const
Finds all the entries in the given range, and possibly a few just outside the range on the ends.
long NumValues() const
The number of data points in the data referred to by the kvector.
Definition databases.hpp:30
const unsigned char * SubDatabasePointer(int32_t magicValue) const
MultiDatabase memory layout:
long NumPairs() const
Exact number of stored pairs.
std::vector< decimal > StarDistances(int16_t star, const Catalog &) const
Return the distances from the given star to each star it's paired with in the database (for debugging...
PairDistanceKVectorDatabase(DeserializeContext *des)
Create the database from a serialized buffer.
static const int32_t kMagicValue
Magic value to use when storing inside a MultiDatabase.
Definition databases.hpp:70
const int16_t * FindPairsExact(const Catalog &, decimal min, decimal max, const int16_t **end) const
const int16_t * FindPairsLiberal(decimal min, decimal max, const int16_t **end) const
Return at least all the star pairs whose inter-star distance is between min and max.
#define MULTI_DB_FLOAT_FLAG
A database that contains multiple databases This is almost always the database that is actually passe...
double decimal
Definition decimal.hpp:11
#define DECIMAL(x)
Definition decimal.hpp:21
#define DECIMAL_M_PI
Definition decimal.hpp:29
#define DECIMAL_COS(x)
Definition decimal.hpp:53
LOST starting point.
void SerializeKVectorIndex(SerializeContext *ser, const std::vector< decimal > &values, decimal min, decimal max, long numBins)
K-vector index layout.
Definition databases.cpp:64
decimal AngleUnit(const Vec3 &vec1, const Vec3 &vec2)
Calculate the inner angle, in radians, between two /unit/ vectors.
bool CompareKVectorPairs(const KVectorPair &p1, const KVectorPair &p2)
Definition databases.cpp:29
std::vector< KVectorPair > CatalogToPairDistances(const Catalog &catalog, decimal minDistance, decimal maxDistance)
pair K-vector database layout.
void SerializePairDistanceKVector(SerializeContext *ser, const Catalog &catalog, decimal minDistance, decimal maxDistance, long numBins)
Serialize a pair-distance KVector into buffer.
bool isFlagSet(uint32_t dbFlags, uint32_t flag)
Definition databases.cpp:19
void SerializePrimitive(SerializeContext *ser, const T &val)
std::vector< MultiDatabaseEntry > MultiDatabaseDescriptor
void SerializeMultiDatabase(SerializeContext *ser, const MultiDatabaseDescriptor &dbs, uint32_t flags)
decimal Clamp(decimal num, decimal low, decimal high)
Return the value in the range [low,high] which is closest to num.
std::vector< CatalogStar > Catalog