Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 37 additions & 13 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,24 +1,48 @@
CC ?= cc
# Minimal Makefile: only build and run the unit test binary.
CFLAGS ?= -O2 -Iinclude -Wall -Wextra -std=c11
AR ?= ar
OPT ?= -O2
CFLAGS ?= -std=c99 -Wall -Wextra -Iinclude
BUILD_DIR = build
CTEST_PATH = $(BUILD_DIR)/tests/ctest

all: ctest
LIB = $(BUILD_DIR)/libcuc.a
OBJ = $(BUILD_DIR)/src/cuc.o
CTEST = $(BUILD_DIR)/tests/ctest
EXAMPLE = $(BUILD_DIR)/examples/cuc_example

ctest: $(CTEST_PATH)
SRC = src/cuc.c
HDR = include/cuc.h

$(CTEST_PATH): tests/unit_tests.c
all: $(LIB) $(CTEST) $(EXAMPLE)

lib: $(LIB)

$(OBJ): $(SRC) $(HDR)
mkdir -p $(dir $@)
$(CC) $(CFLAGS) -Iinclude tests/unit_tests.c -o $(CTEST_PATH)
$(CC) $(CFLAGS) $(OPT) -Iinclude -c $(SRC) -o $@

run: ctest
$(CTEST_PATH)
$(LIB): $(OBJ)
mkdir -p $(dir $@)
$(AR) rcs $@ $(OBJ)

clean:
rm -rf $(BUILD_DIR)
ctest: $(CTEST)

$(CTEST): tests/unit_tests.c tests/test_cuc.c tests/cunit.h tests/test_runners.h $(SRC) $(HDR)
mkdir -p $(dir $@)
$(CC) $(CFLAGS) $(OPT) -Iinclude -Itests tests/unit_tests.c tests/test_cuc.c $(SRC) -o $@

example: $(EXAMPLE)

$(EXAMPLE): examples/cuc_example.c $(SRC) $(HDR)
mkdir -p $(dir $@)
$(CC) $(CFLAGS) $(OPT) -Iinclude examples/cuc_example.c $(SRC) -o $@

run: $(CTEST)
$(CTEST)

coverage-html:
bash tools/coverage_html.sh
bash tools/coverage-html.sh

clean:
rm -rf $(BUILD_DIR)

.PHONY: all ctest run clean
.PHONY: all lib ctest example run coverage-html clean
192 changes: 118 additions & 74 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,89 +1,84 @@
# ProjectName
Project description.
Template repo for minimal embedded C implementations of CCSDS / ECSS standards.
# EmbeddedCUCTime

A minimal, embedded-friendly C library implementing the **CCSDS Unsegmented
Time Code (CUC)** — a pure binary count of seconds and binary fractions of a
second from a defined epoch.

The library is deliberately small and dependency-free (only `<stdint.h>`,
`<stddef.h>`, `<stdbool.h>`), uses caller-supplied buffers (no heap), and keeps
its core encode/decode path integer-only so it fits comfortably on
microcontrollers.

## Standards Compliance

- **CCSDS 000.0-X-Y**:
- **CCSDS 301.0-B-4** — *Time Code Formats*, Section 3.2 (CCSDS Unsegmented
Time Code). See [`docs/ccsds_cuc.md`](docs/ccsds_cuc.md) for a field-by-field
summary.

## Features

### Core Protocol Implementation


### Design Principles

- Encode/decode the **P-field** (1 or 2 octets) and **T-field** separately, or
as a combined self-identified code.
- Full format range: 1–7 basic (second) octets, 0–10 fractional octets.
- Both CCSDS 1958 TAI epoch (Level 1) and agency-defined epoch (Level 2).
- Format-independent time value (`cuc_time_t`) using a Q0.64 binary fraction.
- Optional `double` conversion helpers, removable with `-DCUC_NO_FLOAT`.
- Explicit, checked error codes; no dynamic allocation.

## Project Structure

```
EmbeddedSpacePacket/
EmbeddedCUCTime/
├── include/
│ └──
│ └── cuc.h # Public API
├── src/
│ └──
│ └── cuc.c # Implementation
├── examples/
│ └──
│ └── cuc_example.c # Encode/decode demo
├── tests/
│ ├── cunit.h # Minimal test framework
│ └── unit_tests.c # Unit tests
├── scripts/
│ └── coverage_html.sh # Coverage report
├── build/ # Build artifacts
├── docs/
│ └── ccsds_cuc.md # CUC format reference notes
├── tools/
│ └── coverage-html.sh # Coverage report helper
├── Makefile
└── README.md
```

## Building

### Build Everything
### Build everything

```bash
make
make # static library, example and test binaries in build/
```

Builds the static library, the example binary and the test binary in `build/`.

### Build Library Only
### Build the library only

```bash
make lib
# Produces: build/
make lib # produces build/libcuc.a
```

### Build Example
### Build and run the example

```bash
make example
./build/examples/example
./build/examples/cuc_example
```

### Run Tests
### Run the tests

```bash
make ctest
./build/tests/ctest
make run # or: make ctest && ./build/tests/ctest
```

### Coverage (HTML)

Requires `gcovr` installed in your system:

```bash
sudo apt install gcovr
```

Generate coverage report:
Requires `gcovr` (`pip install gcovr`):

```bash
make coverage-html
```

Output report:

```bash
build/coverage/index.html
make coverage-html # writes build/coverage/index.html
```

### Clean
Expand All @@ -94,59 +89,108 @@ make clean

## Quick Start

### Step 1

```c
#include "cuc.h"

```
/* 4 octets of seconds + 2 octets of fraction, CCSDS 1958 TAI epoch. */
cuc_format_t fmt = {CUC_EPOCH_CCSDS, 4, 2};
cuc_time_t t = cuc_time_from_seconds(1234567.25);

### Step 2
uint8_t buf[CUC_OCTETS_MAX];
size_t n = 0;

```c
if (cuc_encode(&t, &fmt, buf, sizeof(buf), &n) == CUC_OK) {
/* buf[0..n-1] holds P-field + T-field: 1E 00 12 D6 87 40 00 */
}

cuc_format_t out_fmt;
cuc_time_t out;
size_t consumed = 0;
cuc_decode(buf, n, &out_fmt, &out, &consumed); /* out ≈ 1234567.25 s */
```

### Step X

To work with an implicitly-conveyed P-field (structure known from external
metadata), use `cuc_tfield_encode` / `cuc_tfield_decode` with a caller-supplied
`cuc_format_t` and omit the P-field entirely.

## API Reference

### Lifecycle
### Types

```c
typedef enum { CUC_EPOCH_CCSDS = 1, CUC_EPOCH_AGENCY = 2 } cuc_epoch_t;

```
typedef struct {
cuc_epoch_t epoch;
uint8_t basic_octets; /* 1..7 */
uint8_t fraction_octets; /* 0..10 */
} cuc_format_t;

### Building a Packet

```c
typedef struct {
uint64_t seconds; /* integer seconds since epoch */
uint64_t fraction; /* binary fraction of a second, Q0.64 */
} cuc_time_t;

typedef enum {
CUC_OK = 0, CUC_ERR_NULL, CUC_ERR_BUFFER,
CUC_ERR_FORMAT, CUC_ERR_PFIELD_ID, CUC_ERR_UNSUPPORTED
} cuc_status_t;
```

### Utilities
### Functions

```c

```

### Types

```c

```

## Memory Usage (Estimated)

- **Library (stripped)**:
- **Serialization buffer**:
- **No heap usage**: all allocations are caller-supplied

## CCSDS XXX — Notes

## Limitations
/* Sizing / validation */
cuc_status_t cuc_format_validate(const cuc_format_t *fmt);
size_t cuc_pfield_size(const cuc_format_t *fmt); /* 1 or 2 */
size_t cuc_tfield_size(const cuc_format_t *fmt); /* basic + frac */
size_t cuc_size(const cuc_format_t *fmt); /* P + T */

/* P-field only */
cuc_status_t cuc_pfield_encode(const cuc_format_t *fmt, uint8_t *buf,
size_t buf_len, size_t *written);
cuc_status_t cuc_pfield_decode(const uint8_t *buf, size_t buf_len,
cuc_format_t *fmt, size_t *consumed);

/* T-field only (implicit P-field) */
cuc_status_t cuc_tfield_encode(const cuc_time_t *time, const cuc_format_t *fmt,
uint8_t *buf, size_t buf_len, size_t *written);
cuc_status_t cuc_tfield_decode(const uint8_t *buf, size_t buf_len,
const cuc_format_t *fmt, cuc_time_t *time,
size_t *consumed);

/* Combined self-identified code (P-field + T-field) */
cuc_status_t cuc_encode(const cuc_time_t *time, const cuc_format_t *fmt,
uint8_t *buf, size_t buf_len, size_t *written);
cuc_status_t cuc_decode(const uint8_t *buf, size_t buf_len, cuc_format_t *fmt,
cuc_time_t *time, size_t *consumed);

/* Convenience (omitted with -DCUC_NO_FLOAT) */
double cuc_time_to_seconds(const cuc_time_t *time);
cuc_time_t cuc_time_from_seconds(double seconds);
```

## Memory Usage

- **No heap usage**: all buffers are caller-supplied.
- **Largest CUC code**: `CUC_OCTETS_MAX` = 19 octets (2 P-field + 17 T-field).
- **State**: `cuc_time_t` and `cuc_format_t` are small plain structs.

## Notes and Limitations

- CUC is **not UTC-based**; leap-second corrections do not apply (use CDS/CCS
for UTC-based time — not yet implemented here).
- The fractional value is stored to 64 bits (Q0.64). Encodings that request
more than 8 fractional octets carry those extra low octets as zero.
- Integer seconds fit in `uint64_t`; a field configured for fewer octets rolls
over modulo 256 per octet, exactly as the standard specifies.
- This library implements the CUC format only. CDS, CCS and ASCII time codes
from CCSDS 301.0-B-4 are out of scope for this initial version.

## References

- CCSDS 301.0-B-4, *Time Code Formats*, Blue Book, Issue 4, November 2010.

## License

See LICENSE file.
See the [LICENSE](LICENSE) file.
Binary file added docs/301x0b4e1_time.pdf
Binary file not shown.
50 changes: 50 additions & 0 deletions examples/cuc_example.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/**
* @file cuc_example.c
* @brief Minimal usage example for the CUC time code library
*
* Encodes a time value into a self-identified CUC code and decodes it back,
* printing the intermediate octets. Demonstrates CCSDS 301.0-B-4, Section 3.2.
*
* OpenSpaceCode — https://github.com/OpenSpaceCode
*/

#include "cuc.h"

#include <stdio.h>

int main(void)
{
/* 4 octets of seconds, 2 octets of fraction, CCSDS 1958 TAI epoch. */
cuc_format_t fmt = {CUC_EPOCH_CCSDS, 4, 2};
cuc_time_t time = cuc_time_from_seconds(1234567.25);

uint8_t buf[CUC_OCTETS_MAX];
size_t written = 0;
if (cuc_encode(&time, &fmt, buf, sizeof(buf), &written) != CUC_OK)
{
fprintf(stderr, "encode failed\n");
return 1;
}

printf("encoded %zu octets:", written);
for (size_t i = 0; i < written; i++)
{
printf(" %02X", buf[i]);
}
printf("\n");

cuc_format_t decoded_fmt;
cuc_time_t decoded;
size_t consumed = 0;
if (cuc_decode(buf, written, &decoded_fmt, &decoded, &consumed) != CUC_OK)
{
fprintf(stderr, "decode failed\n");
return 1;
}

printf("decoded: %.3f s (%u basic + %u fractional octets)\n",
cuc_time_to_seconds(&decoded),
decoded_fmt.basic_octets,
decoded_fmt.fraction_octets);
return 0;
}
Loading