Sto scrivendo un software che si occupa di effemeridi - tabelle che descrivono le posizioni e gli orientamenti dei corpi celesti o dei veicoli spaziali - e io sto lottando con il modo di rappresentarli nel mio codice.
Alcuni background: gli input di questo software sono un insieme di file di effemeridi scelti dall'utente, in vari formati di file. Devo tenere traccia di quali file vengono utilizzati in un determinato calcolo. Ho una generica File
superclasse con sottoclassi per ciascuno dei formati di file.
Ho anche effemeridi derivati da uno o più file; per esempio. un file fornisce il vettore Earth-to-spacecraft e un altro fornisce il vettore Sun-to-Earth, e combinando i due ottengo il vettore veicolo spaziale-Sole.
Ho due diversi schemi di rappresentazione a cui sto pensando:
-
Gli oggetti effemeridi rappresentano una singola coppia di oggetti. Ad esempio, la posizione da Terra a spaziale o l'orientamento di un veicolo spaziale. In questo schema, un oggetto file memorizza uno o più oggetti effemeridi e un oggetto effimero ha uno o più genitori (file o altre effemeridi). L'interfaccia sarebbe simile a questa:
class File { /* metadata not shown */ EphemerisIterator beginEphemerides(); EphemerisIterator endEphemerides(); } class Ephemeris { Timestamp startTime(); /* time range for which the ephemeris is valid */ Timestamp stopTime(); FileIterator beginParentFiles(); /* iterates recursively over parent ephemerides */ FileIterator endParentFiles(); } class PositionEphemeris : Ephemeris { Object source(); /* Object is an enum with values like Object::Earth */ Object target(); Frame frame(); /* coordinate reference frame, e.g. Frame::J2000 */ Vector3 position(Timestamp); /* vector from source() to target() */ } class AttitudeEphemeris : Ephemeris { Frame source(); Frame target(); Quaternion attitude(Timestamp); /* rotation quaternion from source() to target() */ }
Uno svantaggio di questo schema è che il consumatore dell'oggetto
File
deve cercare attraverso un elenco di effemeridi per trovare quello che sta cercando. Un file può contenere un sacco di effemeridi, ad es. le effemeridi del sistema solare che sto usando contengono le posizioni degli otto pianeti, oltre a Plutone, il Sole e la Luna, ma spesso ne contiene solo una.Un altro svantaggio è che nel caso a più effemeridi, l'utente deve costruire in modo esplicito effemeridi compositi, ad es. per ottenere da Terra a Sole le effemeridi del sistema solare, devi combinare SSB (baricentro del sistema solare) -da-Sole, SSB-to-EMB (baricentro Terra-Luna) ed EMB-to-Terra.
-
Disponi di un archivio di oggetti effemeridi per più oggetti. In questo caso gli oggetti
File
eEphemeris
potrebbero avere una corrispondenza uno-a-uno, quindi una classe concreta per un determinato file formato potrebbe sottoclasse entrambi.class File { /* metadata not shown */ } class Ephemeris { FileIterator beginParentFiles(); /* iterates recursively over parent ephemerides */ FileIterator endParentFiles(); } class PositionEphemeris : Ephemeris { /* vector from source to target in frame */ Vector3 position(Timestamp, Object source, Object target, Frame frame); } class AttitudeEphemeris : Ephemeris { /* rotation quaternion from source to target */ Quaternion attitude(Timestamp, Frame source, Frame target); }
In questo schema il lavoro di ricerca e combinazione delle effemeridi corrette è completamente curato dall'oggetto
Ephemeris
; tuttavia, la ricerca deve essere eseguita ogni volta che vengono chiamatiposition
oattitude
, il che può verificarsi in un ciclo interno ristretto (ad es. per il reperimento delle radici). Inoltre ora è necessario introdurre la gestione delle eccezioni nel caso in cui l'origine, la destinazione e / o il frame specificati non siano validi per le effemeridi specificate.Un altro svantaggio è che le effemeridi compositi richiedono un po 'più di lavoro da costruire. Prendi un
ChainedPositionEphemeris
: nello schema 1 potrebbe essere qualcosa del genere:class ChainedPositionEphemeris : PositionEphemeris { PositionEphemeris _ephem1, _ephem2; ChainedPositionEphemeris(PositionEphemeris e1, PositionEphemeris e2) : _ephem1(e1), _ephem2(e2) { assert(_ephem1.target() == _ephem2.source()); assert(_ephem1.frame() == _ephem2.frame()); } Timestamp startTime() { return max(_ephem1.startTime(), _ephem2.startTime()); } Timestamp stopTime() { return min(_ephem1.stopTime(), _ephem2.stopTime()); } /* beginParentFiles and endParentFiles omitted */ Object source() { return _ephem1.source(); } Object target() { return _ephem2.target(); } Frame frame() { return _ephem1.frame(); } Vector3 position(Timestamp t) { return _ephem1.position(t) + _ephem2.position(t); } }
Al contrario, l'implementazione per il secondo schema dovrebbe essere simile a questa:
class ChainedPositionEphemeris : PositionEphemeris { PositionEphemeris _base, _extension; Object _sharedObject; Set<Object> _extendedObjects; /* constructor omitted */ Vector3 position(Timestamp t, Object source, Object target, Frame frame) { bool sourceIsExtended = source == _sharedObject || _extendedObjects.contains(source); bool targetIsExtended = target == _sharedObject || _extendedObjects.contains(target); if(sourceIsExtended && targetIsExtended) return _extension.position(t, source, object, frame); else if(sourceIsExtended) return _extension.position(t, source, _sharedObject, frame) + _base.position(t, _sharedObject, target, frame); else if(targetIsExtended) return _extension.position(t, _sharedObject, target, frame) + _base.position(t, source, _sharedObject, frame); else return _base.position(t, source, object, frame); } }
Un ulteriore piccolo svantaggio è che una singola effemeride può contenere dati non correlati da più fonti, rendendo
startTime
estopTime
senza significato.
Vorrei le tue opinioni su quale schema porterebbe a un codice più facile da gestire, o se c'è una rappresentazione migliore a cui non avessi pensato.