Skip to content
Merged
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
26 changes: 26 additions & 0 deletions Software/oscillatord/src/oscillators/sa5x_oscillator.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#define MAX_SERIALNUM_LENGTH 11
#define DIGITAL_TUNING_MAX 20000000LL
#define DEFAULT_PHASELIMIT 100000 // Phase limit is 100us
#define REQUIRED_PPS_WIDTH 80000000 // PPS output width is 80ms
#define NO_GNSS_FIX_TIMEOUT 9 // seconds after last gnss Fix befor holdover
#define BIT(nr) (1UL << (nr))

Expand Down Expand Up @@ -48,6 +49,8 @@
#define CMD_SET_DISCIPLINING "{set,Disciplining,%d}"
#define CMD_GET_PHASELIMIT "{get,PhaseLimit}"
#define CMD_SET_PHASELIMIT "{set,PhaseLimit,%d}"
#define CMD_GET_PPS_WIDTH "{get,PpsWidth}"
#define CMD_SET_PPS_WIDTH "{set,PpsWidth,%d}"

#define DISCIPLINING_PHASES 3

Expand Down Expand Up @@ -307,6 +310,28 @@ static int sa5x_oscillator_read_phase(int32_t *val, int size)
return res;
}

static void sa5x_oscillator_configure_pps_width(struct sa5x_oscillator *sa5x)
{
int cmd_len, err, pps_width;

err = sa5x_oscillator_read_intval(&pps_width,
sa5x_oscillator_cmd(sa5x, CMD_GET_PPS_WIDTH,
sizeof(CMD_GET_PPS_WIDTH)));
if (err > 0 && pps_width == REQUIRED_PPS_WIDTH)
return;

if (err > 0)
log_info("SA5x reports PPS width %d ns, updating to %d ns",
pps_width, REQUIRED_PPS_WIDTH);
else
log_warn("SA5x: cannot get PPS width, applying the required value");

cmd_len = snprintf(answer_str, answer_len, CMD_SET_PPS_WIDTH,
REQUIRED_PPS_WIDTH);
if (sa5x_oscillator_cmd(sa5x, answer_str, cmd_len) == -1)
log_warn("SA5x: couldn't configure PPS width");
}

static int sa5x_oscillator_get_attributes(struct oscillator *oscillator, struct sa5x_attributes *a,
unsigned int attributes_mask)
{
Expand Down Expand Up @@ -460,6 +485,7 @@ static struct oscillator *sa5x_oscillator_new(struct devices_path *devices_path)
if (!sa5x_oscillator_get_attributes(oscillator, NULL, ATTR_FW_SERIAL)) {
log_debug("connected to MAC with serial %s, fw: %20s", sa5x->serial, sa5x->version);
}
sa5x_oscillator_configure_pps_width(sa5x);

sa5x->status.clock_class = SA5X_CLOCK_CLASS_CALIBRATING;
sa5x->status.status = SA5X_INIT;
Expand Down
10 changes: 10 additions & 0 deletions Software/oscillatord/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ if(BUILD_TESTS)
file(GLOB EXTTS_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/extts.[ch])
file(GLOB CONFIG_DISCOVERY_TEST_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/config_discovery_test.c)
file(GLOB SA5X_OSCILLATOR_TEST_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/sa5x_oscillator_test.c
${PROJECT_SOURCE_DIR}/src/oscillator.[ch]
${PROJECT_SOURCE_DIR}/src/oscillator_factory.[ch]
${PROJECT_SOURCE_DIR}/src/oscillators/sa5x_oscillator.c)


pkg_check_modules(SYSTEMD REQUIRED libsystemd)
Expand All @@ -57,6 +62,9 @@ if(BUILD_TESTS)
)
add_executable(extts_test ${EXTTS_TEST_SOURCES} ${COMMON_SOURCES} ${EXTTS_SOURCES})
add_executable(config_discovery_test ${CONFIG_DISCOVERY_TEST_SOURCES} ${COMMON_SOURCES})
add_executable(sa5x_oscillator_test
${SA5X_OSCILLATOR_TEST_SOURCES}
${COMMON_SOURCES})

target_link_libraries(oscillator_sim PRIVATE m)
target_link_libraries(mro50_ctrl PRIVATE m)
Expand All @@ -73,8 +81,10 @@ if(BUILD_TESTS)
target_link_libraries(extts_test PRIVATE
m)
target_link_libraries(config_discovery_test PRIVATE m)
target_link_libraries(sa5x_oscillator_test PRIVATE m Threads::Threads)

add_test(NAME config_discovery COMMAND config_discovery_test)
add_test(NAME sa5x_oscillator COMMAND sa5x_oscillator_test)

install(TARGETS oscillator_sim RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
install(TARGETS mro50_ctrl RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
Expand Down
116 changes: 116 additions & 0 deletions Software/oscillatord/tests/sa5x_oscillator_test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
#include <errno.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <fcntl.h>
#include <pthread.h>

#include "config.h"
#include "oscillator_factory.h"

#define CHECK(condition) \
do { \
if (!(condition)) { \
fprintf(stderr, "CHECK failed at %s:%d: %s\n", __FILE__, __LINE__, #condition); \
exit(EXIT_FAILURE); \
} \
} while (0)

struct command_response {
const char* command;
const char* response;
};

struct responder {
int fd;
const struct command_response* sequence;
size_t count;
};

static void* respond_to_commands(void* argument) {
struct responder* responder = argument;
size_t index;

for (index = 0; index < responder->count; index++) {
const struct command_response* expected = &responder->sequence[index];
char command[128] = {0};
ssize_t length;

do {
length = read(responder->fd, command, sizeof(command) - 1);
} while (length < 0 && errno == EINTR);
CHECK(length > 0);
while (length > 0 && command[length - 1] == '\0')
length--;
command[length] = '\0';
CHECK(strcmp(command, expected->command) == 0);
CHECK(write(responder->fd, expected->response, strlen(expected->response))
== (ssize_t)strlen(expected->response));
}

return NULL;
}

static void test_pps_width(bool expect_update) {
const struct command_response width_update_sequence[] = {
{ "\\{swrev?}", "[=V1.1,test]\r\n"},
{ "{serial?}", "[=12345678901]\r\n"},
{ "{get,PhaseLimit}", "[=100000]\r\n"},
{ "{get,PpsWidth}", "[=40000000]\r\n"},
{"{set,PpsWidth,80000000}", "[=80000000]\r\n"},
{ "{set,TauPps0,50}", "[=50]\r\n"},
};
const struct command_response width_already_set_sequence[] = {
{ "\\{swrev?}", "[=V1.1,test]\r\n"},
{ "{serial?}", "[=12345678901]\r\n"},
{"{get,PhaseLimit}", "[=100000]\r\n"},
{ "{get,PpsWidth}", "[=80000000]\r\n"},
{"{set,TauPps0,50}", "[=50]\r\n"},
};
const struct command_response* sequence = expect_update ? width_update_sequence :
width_already_set_sequence;
struct responder responder = {
.sequence = sequence,
.count = expect_update ?
sizeof(width_update_sequence) / sizeof(width_update_sequence[0]) :
sizeof(width_already_set_sequence) / sizeof(width_already_set_sequence[0]),
};
struct devices_path devices = {0};
struct config config = {0};
struct oscillator* oscillator;
char slave_path[128];
pthread_t thread;
int master_fd, slave_fd;

master_fd = posix_openpt(O_RDWR | O_NOCTTY);
CHECK(master_fd >= 0);
CHECK(grantpt(master_fd) == 0);
CHECK(unlockpt(master_fd) == 0);
CHECK(ptsname_r(master_fd, slave_path, sizeof(slave_path)) == 0);
slave_fd = open(slave_path, O_RDWR | O_NOCTTY);
CHECK(slave_fd >= 0);

responder.fd = master_fd;
CHECK(pthread_create(&thread, NULL, respond_to_commands, &responder) == 0);
CHECK(config_set(&config, "oscillator", "sa5x") == 0);
CHECK(snprintf(devices.mac_path, sizeof(devices.mac_path), "%s", slave_path) > 0);

oscillator = oscillator_factory_new(&config, &devices);
CHECK(oscillator != NULL);
oscillator_factory_destroy(&oscillator);
CHECK(pthread_join(thread, NULL) == 0);

config_cleanup(&config);
close(slave_fd);
close(master_fd);
}

int main(void) {
test_pps_width(true);
test_pps_width(false);
return 0;
}
Loading