Skip to content
Open
41 changes: 40 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ set(CMAKE_CXX_EXTENSIONS OFF)

set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

# Enable AVX2 optimizations (without -march=native to avoid ISA mismatches)
if (NOT CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mavx2 -mfma -fopenmp")
endif()

if (NOT CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wextra")
Expand All @@ -24,7 +29,6 @@ if (NOT CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-long-long")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wcast-align")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wsign-conversion")
endif(NOT CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")

set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
Expand All @@ -36,4 +40,39 @@ add_subdirectory(sattrack)
add_subdirectory(runtest)
add_subdirectory(passpredict)

# Optional test programs not present in this repo snapshot are skipped.
# add_executable(residual_trace residual_trace.cc)
# target_link_libraries(residual_trace sgp4)
#
# add_executable(test_bstar test_bstar.cc)
# target_link_libraries(test_bstar sgp4)
#
# add_executable(testbench testbench.cc)
# target_link_libraries(testbench sgp4)
#
add_executable(full_test full_test.cc)
target_link_libraries(full_test sgp4)

add_executable(benchmark benchmark.cc)
target_link_libraries(benchmark sgp4)

# Optional test programs not present in this repo snapshot are skipped.
# add_executable(baseline_test baseline_test.cc)
# target_link_libraries(baseline_test sgp4)
#
# add_executable(calibrate_k calibrate_k.cc)
# target_link_libraries(calibrate_k sgp4)
#
# add_executable(oracle_test oracle_test.cc)
# target_link_libraries(oracle_test sgp4)
#
# add_executable(piecewise_test piecewise_test.cc)
# target_link_libraries(piecewise_test sgp4)
#
# add_executable(multiyear_bench multiyear_bench.cc)
# target_link_libraries(multiyear_bench sgp4)
#
# add_executable(trace_cpp trace_cpp.cc)
# target_link_libraries(trace_cpp sgp4)

file(COPY SGP4-VER.TLE DESTINATION ${PROJECT_BINARY_DIR})
141 changes: 129 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,136 @@
SGP4 library
SGP4 Library
============

License
This repository contains a C++ SGP4 library plus a set of experimental acceleration components for batch propagation and conjunction-screening workflows.

The current branch builds around two main paths:

- A scalar SGP4 implementation in libsgp4::SGP4.
- A batch-oriented propagator in libsgp4::SGP4Batch with SoA storage, OpenMP parallelism, and SIMD-capable code paths with scalar and AVX2 fallbacks.

What Changed
------------

The repository was cleaned up to build on the current Linux toolchain without compile errors, and the documentation was aligned to the code that is actually present in this branch.

Build and code fixes applied:

- Restored the missing SGP4::SetAlongTrackBias API required by full_test.
- Applied the along-track bias correction in the active libsgp4 SGP4 implementation.
- Removed warning-producing local variable names in SGP4Batch that shadowed member fields.
- Removed an unused benchmark local variable.
- Made SLEEF linkage optional so the project still builds when that library is not installed.

Current Optimization Characteristics
------------------------------------

The codebase includes the following implemented optimization work:

- Structure-of-arrays storage in SGP4Batch for better cache locality.
- Batch propagation interfaces for large TLE sets.
- OpenMP parallel loops in batch and screening utilities.
- SIMD abstraction in SGP4Batch with AVX-512, AVX2, and scalar fallback code paths.
- Experimental runtime JIT kernel generation in JitPropagator.
- Experimental spatial filtering utilities such as LinearBVH, SpatialHash, SpatialPartition, and TemporalPruner.

Important caveats:

- Several performance-oriented utilities are experimental and not wired into the default library API.
- The JIT path currently generates AVX-512-oriented source and is hardware/toolchain dependent.
- Some standalone benchmark and research programs in the repository are environment-specific and are not part of the default build.
- Performance claims depend heavily on CPU ISA support, compiler behavior, and workload shape.

Build
-----

Requirements:

- CMake 3.13 or newer
- A C++17 compiler
- OpenMP support

Optional:

- SLEEF. If installed, CMake will link against it. If not installed, the library still builds.

Build commands:

```bash
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j
```

Validated on the current Linux environment with GCC 13 using AVX2/FMA flags from the project CMake configuration.

Targets
-------

Copyright 2017 Daniel Warner
Default configured targets include:

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
- libsgp4 static library
- libsgp4s shared library
- full_test
- benchmark
- sattrack
- runtest
- passpredict

Usage
-----

Scalar propagation:

```cpp
#include "libsgp4/SGP4.h"
#include "libsgp4/Tle.h"

using namespace libsgp4;

Tle tle("TEST",
"1 25544U 98067A 24001.00000000 .00016717 00000-0 10270-3 0 9996",
"2 25544 51.6417 25.1234 0005000 120.0000 240.0000 15.50000000000000");

SGP4 model(tle);
Eci state = model.FindPosition(60.0);
```

Batch propagation:

```cpp
#include "libsgp4/SGP4Batch.h"

using namespace libsgp4;

std::vector<Tle> tles = /* load TLEs */;
SGP4Batch batch(tles);
std::vector<Eci> results;

batch.Propagate(60.0, results, SGP4Batch::MathMode::Standard);
```

Experimental JIT propagation:

```cpp
#include "libsgp4/JitPropagator.h"

using namespace libsgp4;

SGP4Batch batch(tles);
JitPropagator jit(batch);
std::vector<Eci> results;

jit.Propagate(60.0, batch, results);
```

Repository Notes
----------------

- There are duplicate top-level source files and libsgp4 source files in this repository. The active CMake build uses the libsgp4 sources for the library.
- A committed binary artifact named run_5year_sim_vectorized is present in the repository root. It should not be committed as source control content if you want a clean Git history.

License
-------

http://www.apache.org/licenses/LICENSE-2.0
Copyright 2017 Daniel Warner

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Licensed under the Apache License, Version 2.0.
113 changes: 113 additions & 0 deletions benchmark.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
#include "libsgp4/SGP4.h"
#include "libsgp4/SGP4Batch.h"
#include "libsgp4/Tle.h"
#include "libsgp4/DateTime.h"
#include "libsgp4/Vector.h"
#include <chrono>
#include <iostream>
#include <vector>
#include <fstream>
#include <string>

using namespace libsgp4;
using namespace std::chrono;

void load_tles(const std::string& filename, std::vector<Tle>& tles) {
std::ifstream file(filename);
std::string line;
std::string line1, line2;
while (std::getline(file, line)) {
if (line.empty() || line[0] == '#') continue;
if (line[0] == '1') {
line1 = line.substr(0, 69);
} else if (line[0] == '2') {
line2 = line.substr(0, 69);
try {
tles.emplace_back("Benchmark", line1, line2);
} catch (...) {}
}
}
}

int main(int argc, char* argv[]) {
std::vector<Tle> tles;
load_tles("SGP4-VER.TLE", tles);

if (tles.empty()) {
std::cerr << "No TLEs loaded!" << std::endl;
return 1;
}

// Scale up the satellite count for Multi-Core test
std::vector<Tle> scaled_tles;
for(int i=0; i<300; i++) scaled_tles.insert(scaled_tles.end(), tles.begin(), tles.end());
tles = scaled_tles;

int iterations = 1000;
if (argc > 1) iterations = std::stoi(argv[1]);

std::cout << "Benchmarking " << tles.size() << " satellites and " << iterations << " iterations each..." << std::endl;

// Scalar Benchmark
{
auto start = high_resolution_clock::now();
unsigned long long total_propagations = 0;
#pragma omp parallel for reduction(+:total_propagations)
for (int t = 0; t < (int)tles.size(); ++t) {
SGP4 sgp4(tles[t]);
for (int i = 0; i < iterations; ++i) {
try {
sgp4.FindPosition(static_cast<double>(i));
total_propagations++;
} catch (...) { break; }
}
}
auto end = high_resolution_clock::now();
auto duration = duration_cast<microseconds>(end - start).count();
std::cout << "[Scalar] Time: " << duration/1e6 << "s, Avg: " << (double)duration/total_propagations << "us" << std::endl;
}

// Batch Benchmark
{
SGP4Batch batch(tles);
std::vector<Eci> results;
auto start = high_resolution_clock::now();
unsigned long long total_propagations = 0;
for (int i = 0; i < iterations; ++i) {
batch.Propagate(static_cast<double>(i), results);
for (const auto& eci : results) {
if (eci.Position().x != 0.0) total_propagations++;
}
}
auto end = high_resolution_clock::now();
auto duration = duration_cast<microseconds>(end - start).count();
std::cout << "[Batch] Time: " << duration/1e6 << "s, Avg: " << (double)duration/total_propagations << "us" << std::endl;
}

// Verification
{
SGP4Batch batch(tles);
std::vector<Eci> results;
batch.Propagate(10.0, results);
int checks = 0;
int failures = 0;
for (size_t i = 0; i < tles.size(); ++i) {
SGP4 sgp4(tles[i]);
try {
Eci expected = sgp4.FindPosition(10.0);
OrbitalElements el(tles[i]);
if (el.Period() < 225.0) {
checks++;
double diff = std::abs(expected.Position().x - results[i].Position().x);
if (diff > 1e-6) {
std::cout << "Mismatch for satellite " << i << ": diff=" << diff << std::endl;
failures++;
}
}
} catch(...) {}
}
std::cout << "[Verify] Accuracy check: " << checks << " objects verified, " << failures << " failures." << std::endl;
}

return 0;
}
Loading