diff --git a/services/CMakeLists.txt b/services/CMakeLists.txt index 74e18865..7567a4d4 100644 --- a/services/CMakeLists.txt +++ b/services/CMakeLists.txt @@ -1,4 +1,5 @@ add_subdirectory(crypto) +add_subdirectory(gps) add_subdirectory(security) add_subdirectory(os) add_subdirectory(linux) diff --git a/services/gps/CMakeLists.txt b/services/gps/CMakeLists.txt new file mode 100644 index 00000000..0a8573f9 --- /dev/null +++ b/services/gps/CMakeLists.txt @@ -0,0 +1,10 @@ +add_library(eos_gps STATIC + src/gps_nmea.c +) + +target_compile_definitions(eos_gps PUBLIC EOS_ENABLE_GPS=1) + +target_include_directories(eos_gps PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_SOURCE_DIR}/include +) \ No newline at end of file diff --git a/services/gps/include/eos/gps_nmea.h b/services/gps/include/eos/gps_nmea.h new file mode 100644 index 00000000..d13fe7d3 --- /dev/null +++ b/services/gps/include/eos/gps_nmea.h @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2026 EoS Project +// ISO/IEC 25000 | ISO/IEC/IEEE 15288:2023 + +/** + * @file gps_nmea.h + * @brief EoS GPS Service — NMEA 0183 sentence parsing + * + * Provides parsing of standard NMEA GPRMC sentences into a structured + * position/velocity fix usable by higher-level location services. + */ + +#ifndef EOS_GPS_NMEA_H +#define EOS_GPS_NMEA_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + double latitude; + double longitude; + float altitude; + float speed_knots; + int satellites; + bool fix_valid; +} eos_gps_data_t; + +/** + * Parse a standard NMEA $GPRMC sentence. + * + * @param nmea NUL-terminated NMEA sentence, e.g. + * "$GPRMC,092750.000,A,3746.4949,N,12225.1644,W,0.00,0.00,270526,,,A*7B" + * @param data Output struct populated on success. + * @return 0 on success (data->fix_valid indicates whether the fix is valid), + * -1 on invalid arguments or sentence type, -2 if the sentence has + * too few fields to parse. + */ +int eos_gps_parse_gprmc(const char *nmea, eos_gps_data_t *data); + +#ifdef __cplusplus +} +#endif + +#endif /* EOS_GPS_NMEA_H */ \ No newline at end of file diff --git a/services/gps/src/gps_nmea.c b/services/gps/src/gps_nmea.c index 96e5fe89..f7c46b31 100644 --- a/services/gps/src/gps_nmea.c +++ b/services/gps/src/gps_nmea.c @@ -1,17 +1,14 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2026 EoS Project +// ISO/IEC 25000 | ISO/IEC/IEEE 15288:2023 +#include #include #include #include #include -typedef struct { - double latitude; - double longitude; - float altitude; - float speed_knots; - int satellites; - bool fix_valid; -} eos_gps_data_t; +#if EOS_ENABLE_GPS /* Parse standard NMEA $GPRMC sentence */ int eos_gps_parse_gprmc(const char *nmea, eos_gps_data_t *data) { @@ -62,3 +59,5 @@ int eos_gps_parse_gprmc(const char *nmea, eos_gps_data_t *data) { data->fix_valid = true; return 0; } + +#endif /* EOS_ENABLE_GPS */ \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index d9e27714..987b5470 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -106,6 +106,14 @@ add_executable(test_service test_service.c) target_link_libraries(test_service PRIVATE eos_service) add_test(NAME test_service COMMAND test_service) +# --- test_gps: GPS NMEA sentence parsing --- +add_executable(test_gps test_gps.c) +target_link_libraries(test_gps PRIVATE eos_gps) +if(UNIX AND NOT APPLE) + target_link_libraries(test_gps PRIVATE m) +endif() +add_test(NAME test_gps COMMAND test_gps) + # --- test_debug: GDB stub + core dump --- add_executable(test_debug test_debug.c) target_include_directories(test_debug PRIVATE @@ -173,4 +181,4 @@ add_test(NAME test_devicetree COMMAND test_devicetree) # --- Fuzz targets --- # Opt-in (-DEOS_ENABLE_FUZZING=ON, Clang only); the subdirectory guards itself. -add_subdirectory(fuzz) +add_subdirectory(fuzz) \ No newline at end of file diff --git a/tests/test_gps.c b/tests/test_gps.c new file mode 100644 index 00000000..2fa46c0f --- /dev/null +++ b/tests/test_gps.c @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2026 EoS Project +// ISO/IEC 25000 | ISO/IEC/IEEE 15288:2023 + +#include +#include +#include +#include "eos/gps_nmea.h" + +static void test_gprmc_valid_fix(void) { + const char *nmea = + "$GPRMC,092750.000,A,3746.4949,N,12225.1644,W,0.00,0.00,270526,,,A*7B"; + eos_gps_data_t data; + + assert(eos_gps_parse_gprmc(nmea, &data) == 0); + assert(data.fix_valid); + assert(fabs(data.latitude - 37.7749) < 0.01); + assert(fabs(data.longitude - (-122.4194)) < 0.01); + printf("[PASS] gps gprmc valid fix\n"); +} + +static void test_gprmc_void_fix(void) { + const char *nmea = + "$GPRMC,092750.000,V,3746.4949,N,12225.1644,W,0.00,0.00,270526,,,A*7B"; + eos_gps_data_t data; + + assert(eos_gps_parse_gprmc(nmea, &data) == 0); + assert(!data.fix_valid); + printf("[PASS] gps gprmc void fix\n"); +} + +static void test_gprmc_rejects_wrong_sentence(void) { + eos_gps_data_t data; + assert(eos_gps_parse_gprmc("$GPGGA,092750.000,3746.4949,N", &data) == -1); + printf("[PASS] gps gprmc rejects non-GPRMC sentence\n"); +} + +static void test_gprmc_rejects_null_args(void) { + eos_gps_data_t data; + assert(eos_gps_parse_gprmc(NULL, &data) == -1); + assert(eos_gps_parse_gprmc("$GPRMC,...", NULL) == -1); + printf("[PASS] gps gprmc rejects null args\n"); +} + +static void test_gprmc_rejects_short_sentence(void) { + eos_gps_data_t data; + assert(eos_gps_parse_gprmc("$GPRMC,092750.000,A", &data) == -2); + printf("[PASS] gps gprmc rejects short sentence\n"); +} + +int main(void) { + test_gprmc_valid_fix(); + test_gprmc_void_fix(); + test_gprmc_rejects_wrong_sentence(); + test_gprmc_rejects_null_args(); + test_gprmc_rejects_short_sentence(); + printf("All GPS NMEA tests passed.\n"); + return 0; +} \ No newline at end of file