Created
January 25, 2019 23:54
-
-
Save arrieta/8f2c4d47c3a3c2c6148dcbf78b5df4af to your computer and use it in GitHub Desktop.
TLE Parser
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// -*- coding:utf-8; mode:c++; mode:auto-fill; fill-column:80; -*- | |
/// @file tle.cpp | |
/// @brief Implementation of tle. | |
/// @author J. Arrieta <[email protected]> | |
/// @date January 25, 2019 | |
/// @copyright (C) 2019 Hesslag, Inc. | |
// Component header | |
#include "tle.hpp" | |
// C++ Standard Library | |
#include <cstdlib> | |
#include <iostream> | |
#include <string> | |
// Hesslag Library | |
namespace hesslag { | |
namespace tle { | |
namespace { | |
static constexpr const std::size_t gLINE_LENGTH{70}; | |
static constexpr const std::size_t gRECORD_COUNT{3}; | |
static constexpr const std::size_t gRECORD_LENGTH{gLINE_LENGTH * gRECORD_COUNT}; | |
double simple_double( std::string_view s, int line, int beg, int count ) { | |
const int offset = line * gLINE_LENGTH + beg - 1; | |
char buffer[ 16 ]{'\0'}; | |
std::copy_n( std::next( s.begin(), offset ), count, buffer ); | |
return std::atof( buffer ); | |
} | |
double assumed_decimal_double( std::string_view s, | |
int line, | |
int beg, | |
int count ) { | |
const int offset = line * gLINE_LENGTH + beg - 1; | |
char buffer[ 16 ]{'\0'}; | |
buffer[ 0 ] = '.'; | |
std::copy_n( std::next( s.begin(), offset ), count, buffer + 1 ); | |
return std::atof( buffer ); | |
} | |
double assumed_decimal_exponent_double( std::string_view s, | |
int line, | |
int beg, | |
int count ) { | |
const int offset = line * gLINE_LENGTH + beg - 1; | |
char buffer[ 16 ]{'\0'}; | |
auto loc = &buffer[ 0 ]; | |
s = s.substr( offset, count ); | |
while ( s.front() == ' ' ) { | |
s.remove_prefix( 1 ); | |
} | |
if ( s[ 0 ] == '-' ) { | |
*loc++ = '-'; | |
*loc++ = '.'; | |
s.remove_prefix( 1 ); | |
} else { | |
*loc++ = '.'; | |
}; | |
for ( auto& c : s ) { | |
switch ( c ) { | |
case '+': | |
*loc++ = 'E'; | |
*loc++ = '+'; | |
break; | |
case '-': | |
*loc++ = 'E'; | |
*loc++ = '-'; | |
break; | |
case ' ': | |
break; | |
default: | |
*loc++ = c; | |
} | |
} | |
return std::atof( buffer ); | |
} | |
int simple_int( std::string_view s, int line, int beg, int count ) { | |
const int offset = line * gLINE_LENGTH + beg - 1; | |
char buffer[ 16 ]{'\0'}; | |
std::copy_n( std::next( s.begin(), offset ), count, buffer ); | |
return std::atoi( buffer ); | |
} | |
std::string simple_string( std::string_view s, int line, int beg, int count ) { | |
const int offset = line * gLINE_LENGTH + beg - 1; | |
std::string_view v( std::next( s.begin(), offset ), count ); | |
while ( v.back() == ' ' ) { | |
v.remove_suffix( 1 ); | |
} | |
return {v.data(), v.size()}; | |
} | |
} // anonymous namespace | |
int checksum( std::string_view line ) { | |
int value = 0; | |
for ( int k = 0; k < 68; ++k ) { | |
const auto c = line[ k ]; | |
if ( c == '-' ) { | |
value += 1; | |
} else if ( std::isdigit( c ) ) { | |
value += c - '0'; | |
} | |
} | |
return value % 10; | |
} | |
bool is_valid( std::string_view s ) { | |
bool has_proper_length = s.size() >= gRECORD_LENGTH; | |
if ( has_proper_length ) { | |
bool cksm_1 = checksum( s.substr( 70, 70 ) ) == simple_int( s, 1, 69, 1 ); | |
bool cksm_2 = checksum( s.substr( 140, 70 ) ) == simple_int( s, 2, 69, 1 ); | |
return cksm_1 && cksm_2; | |
} else { | |
return false; | |
} | |
} | |
std::string satellite_name( std::string_view s ) { | |
return simple_string( s, 0, 1, 69 ); | |
} | |
std::string international_designator( std::string_view s ) { | |
return simple_string( s, 1, 10, 8 ); | |
} | |
int satellite_number( std::string_view s ) { return simple_int( s, 1, 3, 5 ); } | |
int epoch_year( std::string_view s ) { return simple_int( s, 1, 19, 2 ); } | |
int element_set_number( std::string_view s ) { | |
return simple_int( s, 1, 65, 4 ); | |
} | |
int revolution_number( std::string_view s ) { | |
return simple_int( s, 2, 64, 5 ); | |
} | |
double epoch_day( std::string_view s ) { return simple_double( s, 1, 21, 12 ); } | |
double first_derivative_mean_motion( std::string_view s ) { | |
return 2.0 * simple_double( s, 1, 34, 10 ); | |
} | |
double second_derivative_mean_motion( std::string_view s ) { | |
return 6.0 * assumed_decimal_exponent_double( s, 1, 45, 8 ); | |
} | |
double drag( std::string_view s ) { | |
return assumed_decimal_exponent_double( s, 1, 54, 8 ); | |
} | |
double inclination( std::string_view s ) { return simple_double( s, 2, 9, 8 ); } | |
double right_ascension( std::string_view s ) { | |
return simple_double( s, 2, 18, 8 ); | |
} | |
double eccentricity( std::string_view s ) { | |
return assumed_decimal_double( s, 2, 27, 7 ); | |
} | |
double argument_periapsis( std::string_view s ) { | |
return simple_double( s, 2, 35, 8 ); | |
} | |
double mean_anomaly( std::string_view s ) { | |
return simple_double( s, 2, 44, 8 ); | |
} | |
double mean_motion( std::string_view s ) { | |
return simple_double( s, 2, 53, 11 ); | |
} | |
} // namespace tle | |
} // namespace hesslag |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// -*- coding:utf-8; mode:c++; mode:auto-fill; fill-column:80; -*- | |
/// @file tle.hpp | |
/// @brief Component tle. | |
/// @author J. Arrieta <[email protected]> | |
/// @date January 25, 2019 | |
/// @copyright (C) 2019 Hesslag, Inc. | |
#pragma once | |
// C++ Standard Library | |
#include <string> | |
// Hesslag Library | |
namespace hesslag { | |
namespace tle { | |
bool is_valid( std::string_view s ); | |
std::string satellite_name( std::string_view s ); | |
std::string international_designator( std::string_view s ); | |
int satellite_number( std::string_view s ); | |
int epoch_year( std::string_view s ); | |
int element_set_number( std::string_view s ); | |
int revolution_number( std::string_view s ); | |
double epoch_day( std::string_view s ); | |
double first_derivative_mean_motion( std::string_view s ); | |
double second_derivative_mean_motion( std::string_view s ); | |
double drag( std::string_view s ); | |
double inclination( std::string_view s ); | |
double right_ascension( std::string_view s ); | |
double eccentricity( std::string_view s ); | |
double argument_periapsis( std::string_view s ); | |
double mean_anomaly( std::string_view s ); | |
double mean_motion( std::string_view s ); | |
} // namespace tle | |
} // namespace hesslag |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// -*- coding:utf-8; mode:c++; mode:auto-fill; fill-column:80; -*- | |
/// @file tle.t.cpp | |
/// @brief Test suite for tle. | |
/// @author J. Arrieta <[email protected]> | |
/// @date January 25, 2019 | |
/// @copyright (C) 2019 Hesslag, Inc. | |
// Component header | |
#include "tle.hpp" | |
// C++ Standard Library | |
// Hesslag Library | |
// Google Test Framework | |
#include <gtest/gtest.h> | |
constexpr auto ISS = | |
"ISS (ZARYA) \n" | |
"1 25544U 98067A 08264.51782528 -.00002182 00000-0 -11606-4 0 2927\n" | |
"2 25544 51.6416 247.4627 0006703 130.5360 325.0288 15.72125391563537\n"; | |
constexpr auto ISAT = | |
"ISAT \n" | |
"1 43879U 18111D 19024.90991605 .00000241 00000-0 26704-4 0 9998\n" | |
"2 43879 97.7268 294.9457 0013986 142.3455 217.8748 14.95877100 4297\n"; | |
using namespace hesslag; | |
TEST( TLE, Validation ) { | |
EXPECT_TRUE( tle::is_valid( ISS ) ); | |
EXPECT_TRUE( tle::is_valid( ISAT ) ); | |
} | |
TEST( TLE, SatelliteName ) { | |
EXPECT_EQ( tle::satellite_name( ISS ), "ISS (ZARYA)" ); | |
EXPECT_EQ( tle::satellite_name( ISAT ), "ISAT" ); | |
} | |
TEST( TLE, SatelliteNumber ) { | |
EXPECT_EQ( tle::satellite_number( ISS ), 25544 ); | |
EXPECT_EQ( tle::satellite_number( ISAT ), 43879 ); | |
} | |
TEST( TLE, InternationalDesignator ) { | |
EXPECT_EQ( tle::international_designator( ISS ), "98067A" ); | |
EXPECT_EQ( tle::international_designator( ISAT ), "18111D" ); | |
} | |
TEST( TLE, EpochYear ) { | |
EXPECT_EQ( tle::epoch_year( ISS ), 8 ); | |
EXPECT_EQ( tle::epoch_year( ISAT ), 19 ); | |
} | |
TEST( TLE, EpochDay ) { | |
EXPECT_EQ( tle::epoch_day( ISS ), 264.51782528 ); | |
EXPECT_EQ( tle::epoch_day( ISAT ), 24.90991605 ); | |
} | |
TEST( TLE, FirstDerivativeMeanMotion ) { | |
EXPECT_EQ( tle::first_derivative_mean_motion( ISS ), -.00002182 * 2 ); | |
EXPECT_EQ( tle::first_derivative_mean_motion( ISAT ), .00000241 * 2 ); | |
} | |
TEST( TLE, SecondDerivativeMeanMotion ) { | |
EXPECT_EQ( tle::second_derivative_mean_motion( ISS ), 0.0 ); | |
EXPECT_EQ( tle::second_derivative_mean_motion( ISAT ), 0.0 ); | |
} | |
TEST( TLE, Drag ) { | |
EXPECT_EQ( tle::drag( ISS ), -0.11606E-4 ); | |
EXPECT_EQ( tle::drag( ISAT ), 0.26704E-4 ); | |
} | |
TEST( TLE, ElementSetNumber ) { | |
EXPECT_EQ( tle::element_set_number( ISS ), 292 ); | |
EXPECT_EQ( tle::element_set_number( ISAT ), 999 ); | |
} | |
TEST( TLE, Inclination ) { | |
EXPECT_EQ( tle::inclination( ISS ), 51.6416 ); | |
EXPECT_EQ( tle::inclination( ISAT ), 97.7268 ); | |
} | |
TEST( TLE, RightAscension ) { | |
EXPECT_EQ( tle::right_ascension( ISS ), 247.4627 ); | |
EXPECT_EQ( tle::right_ascension( ISAT ), 294.9457 ); | |
} | |
TEST( TLE, Eccentricity ) { | |
EXPECT_EQ( tle::eccentricity( ISS ), 0.0006703 ); | |
EXPECT_EQ( tle::eccentricity( ISAT ), 0.0013986 ); | |
} | |
TEST( TLE, ArgumentPeriapsis ) { | |
EXPECT_EQ( tle::argument_periapsis( ISS ), 130.5360 ); | |
EXPECT_EQ( tle::argument_periapsis( ISAT ), 142.3455 ); | |
} | |
TEST( TLE, MeanAnomaly ) { | |
EXPECT_EQ( tle::mean_anomaly( ISS ), 325.0288 ); | |
EXPECT_EQ( tle::mean_anomaly( ISAT ), 217.8748 ); | |
} | |
TEST( TLE, MeanMotion ) { | |
EXPECT_EQ( tle::mean_motion( ISS ), 15.72125391 ); | |
EXPECT_EQ( tle::mean_motion( ISAT ), 14.9587710 ); | |
} | |
TEST( TLE, RevolutionNumber ) { | |
EXPECT_EQ( tle::revolution_number( ISS ), 56353 ); | |
EXPECT_EQ( tle::revolution_number( ISAT ), 429 ); | |
} | |
int main( int argc, char** argv ) { | |
::testing::InitGoogleTest( &argc, argv ); | |
return RUN_ALL_TESTS(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment