add tests, refine arduino program

This commit is contained in:
Jeff Epler 2021-11-05 08:41:08 -05:00
parent d947fcbced
commit a4e875b7e3
6 changed files with 190 additions and 24 deletions

1
.gitignore vendored
View file

@ -5,3 +5,4 @@
*.o *.o
decoder decoder
firmware firmware
tests

View file

@ -3,7 +3,7 @@
# SPDX-License-Identifier: GPL-3.0-only # SPDX-License-Identifier: GPL-3.0-only
FIRMWARE = firmware/cwwvb.ino.elf FIRMWARE = firmware/cwwvb.ino.elf
all: decoder $(FIRMWARE) all: decoder $(FIRMWARE) run-tests
decoder: decoder.cpp Makefile decoder.h decoder: decoder.cpp Makefile decoder.h
g++ -Wall -g -Og -o $@ $< -DMAIN g++ -Wall -g -Og -o $@ $< -DMAIN
@ -18,3 +18,14 @@ PORT := /dev/ttyACM0
.PHONY: flash .PHONY: flash
flash: $(FIRMWARE) flash: $(FIRMWARE)
arduino-cli upload -b adafruit:samd:adafruit_feather_m4 -i $(FIRMWARE:.elf=.hex) -p $(PORT) arduino-cli upload -b adafruit:samd:adafruit_feather_m4 -i $(FIRMWARE:.elf=.hex) -p $(PORT)
.PHONY: clean
clean:
rm -f *.o decoder firmware
.PHONY: run-tests
run-tests: tests
./tests
tests: decoder.cpp decoder.h Makefile tests.cpp
g++ -Wall -g -Og -o $@ $(filter %.cpp, $^)

View file

@ -26,6 +26,8 @@ SAMDTimer ITimer0(TIMER_TC3);
int cc; int cc;
int tick_subsec;
WWVBDecoder<> dec; WWVBDecoder<> dec;
constexpr int CENTRAL_COUNT = 3000000 / dec.SUBSEC; constexpr int CENTRAL_COUNT = 3000000 / dec.SUBSEC;
static_assert(CENTRAL_COUNT <= 65535); static_assert(CENTRAL_COUNT <= 65535);
@ -75,7 +77,7 @@ void TimerHandler0(void) {
digitalWrite(PIN_MON, LOW); digitalWrite(PIN_MON, LOW);
TC3->COUNT16.CC[0].reg = cc; TC3->COUNT16.CC[0].reg = cc;
#if MONITOR_LL #if MONITOR_LL
putc(i ? '#' : '_'); putc(i ? '_' : '#', stderr);
#endif #endif
if (introduced_error.load()) { if (introduced_error.load()) {
introduced_error.fetch_sub(1); introduced_error.fetch_sub(1);
@ -84,7 +86,8 @@ void TimerHandler0(void) {
if (dec.update(i)) { if (dec.update(i)) {
wq.put(try_decode); wq.put(try_decode);
} }
auto subsec = mod_diff<dec.SUBSEC>(dec.sos, dec.SUBSEC - 5);
auto subsec = mod_diff<dec.SUBSEC>(dec.subsec, tick_subsec);
if (subsec == 0) { if (subsec == 0) {
wq.put(tick); wq.put(tick);
} }
@ -102,6 +105,8 @@ void set_tc(int n, bool hold) {
if (n < -(int)CENTRAL_COUNT / 100) if (n < -(int)CENTRAL_COUNT / 100)
n = -(int)CENTRAL_COUNT / 100; n = -(int)CENTRAL_COUNT / 100;
cc = CENTRAL_COUNT + n; cc = CENTRAL_COUNT + n;
moveto(1, 24);
fflush(stdout);
fprintf(stderr, "Steer %+4d CC = %5d I = %+5d P=%+5d %.4s\n", n, cc, ss_I, fprintf(stderr, "Steer %+4d CC = %5d I = %+5d P=%+5d %.4s\n", n, cc, ss_I,
ss_P, hold ? "HOLD" : "INTG"); ss_P, hold ? "HOLD" : "INTG");
} }
@ -158,6 +163,7 @@ void loop() {
digitalWrite(PIN_LED, HIGH); digitalWrite(PIN_LED, HIGH);
wq.take()(); wq.take()();
fflush(stdout);
digitalWrite(PIN_LED, LOW); digitalWrite(PIN_LED, LOW);
} }
@ -223,7 +229,8 @@ void try_decode() {
{ {
char buf[snapshot.SYMBOLS]; char buf[snapshot.SYMBOLS];
for (int i = 0; i < sizeof(buf); i++) { for (int i = 0; i < sizeof(buf); i++) {
buf[i] = '0' + snapshot.symbols.at(i); static const char sym2char[] = "012?";
buf[i] = sym2char[snapshot.symbols.at(i)];
} }
moveto(1, 25); moveto(1, 25);
printf("%.*s health=%3d%%", sizeof(buf), buf, printf("%.*s health=%3d%%", sizeof(buf), buf,
@ -232,19 +239,19 @@ void try_decode() {
if (snapshot.symbols.at(snapshot.SYMBOLS - 1) == 2) { if (snapshot.symbols.at(snapshot.SYMBOLS - 1) == 2) {
if (snapshot.decode_minute(w)) { if (snapshot.decode_minute(w)) {
tick_subsec = mod_diff<snapshot.SUBSEC>(snapshot.sos, 5);
w.advance_minutes(); w.advance_minutes();
ever_set = true; ever_set = true;
display_time();
} }
} }
if (ever_set) {
tick();
}
} }
void tick() { void tick() {
// if (ever_set) {
w.advance_seconds(); w.advance_seconds();
display_time(); display_time();
//}
} }
extern "C" int write(int file, char *ptr, int len); extern "C" int write(int file, char *ptr, int len);

View file

@ -104,8 +104,12 @@ static bool last_yday(int year) { return 365 + isly(year); }
// advance to exactly the top of the n'th minute from now // advance to exactly the top of the n'th minute from now
void wwvb_time::advance_minutes(int n) { void wwvb_time::advance_minutes(int n) {
if (seconds_in_minute() == 61) { if (seconds_in_minute() != 60) {
ls = 0; ls = 0;
if (dut1 < 0)
dut1 += 10;
else if (dut1 > 0)
dut1 -= 10;
} }
second = 0; second = 0;
@ -122,7 +126,7 @@ void wwvb_time::advance_minutes(int n) {
yday++; yday++;
if (dst == 1) if (dst == 1)
dst = 0; dst = 0;
if (dst == 2) else if (dst == 2)
dst = 3; dst = 3;
if (yday < last_yday(year)) if (yday < last_yday(year))
return; return;
@ -137,7 +141,7 @@ void wwvb_time::advance_minutes(int n) {
using namespace std; using namespace std;
int main() { int main() {
WWVBDecoder<120> dec; WWVBDecoder<> dec;
static char zone[] = "TZ=UTC"; static char zone[] = "TZ=UTC";
putenv(zone); putenv(zone);

View file

@ -72,7 +72,7 @@ template <int N, int M> struct circular_symbol_array {
return result; return result;
} }
bool put(int v) { int put(int v) {
int result = 0; int result = 0;
for (int j = 0; j < M; j++) { for (int j = 0; j < M; j++) {
result = (result << 1) | data.put(v & (1 << (M - 1))); result = (result << 1) | data.put(v & (1 << (M - 1)));
@ -89,7 +89,6 @@ struct wwvb_time {
int16_t yday; int16_t yday;
int8_t year, hour, minute, second; int8_t year, hour, minute, second;
int8_t ls, ly, dst, dut1; int8_t ls, ly, dst, dut1;
int8_t month, mday;
time_t to_utc() const; time_t to_utc() const;
struct tm apply_zone_and_dst(int zone_offset, bool observe_dst) const; struct tm apply_zone_and_dst(int zone_offset, bool observe_dst) const;
@ -234,11 +233,16 @@ struct WWVBDecoder {
return expect ? count : length - count; return expect ? count : length - count;
} }
// We're informed that a new second _just started_, so // A second just concluded, so signal.at(BUFFER-1) is the last sample of
// signal.at(BUFFER-1) is the first sample of the new second. and // the second, and signal.at(BUFFER-SUBSEC) is the first sample of the
// signal.at(BUFFER-SUBSEC-1) is the first sample of the previous second // second
void decode_symbol() { void decode_symbol() {
constexpr auto OFFSET = BUFFER - SUBSEC - 1; constexpr auto OFFSET = BUFFER - SUBSEC;
#if 0
for(size_t i=0; i<SUBSEC; i++) {
printf("%c", signal.at(OFFSET + i) ? '_' : '#');
}
#endif
int count_a = count(OFFSET + p0, OFFSET + p1); int count_a = count(OFFSET + p0, OFFSET + p1);
int count_b = count(OFFSET + p1, OFFSET + p2); int count_b = count(OFFSET + p1, OFFSET + p2);
int count_c = count(OFFSET + p2, OFFSET + p3); int count_c = count(OFFSET + p2, OFFSET + p3);
@ -246,16 +250,23 @@ struct WWVBDecoder {
int result = 0; int result = 0;
if (count_c > lc / 2) if (count_c > lc / 2) {
result = 2; if (count_b > lb / 2) {
else if (count_b > lb / 2) result = 2;
} else {
result = 3; // a nonsense symbol
}
} else if (count_b > lb / 2) {
result = 1; result = 1;
}
int h = 0; int h = 0;
h += check_health(count_a, la, 1); if (result != 3) {
h += check_health(count_b, lb, result != 0); h += check_health(count_a, la, 1);
h += check_health(count_c, lc, result == 2); h += check_health(count_b, lb, result != 0);
h += check_health(count_d, ld, 0); h += check_health(count_c, lc, result == 2);
h += check_health(count_d, ld, 0);
}
int sc = symbol_count++; int sc = symbol_count++;
int si = sc % SYMBOLS; int si = sc % SYMBOLS;
@ -264,6 +275,14 @@ struct WWVBDecoder {
health += (h - oh); health += (h - oh);
symbols.put(result); symbols.put(result);
#if 0
printf(" %d\n", result);
for(size_t i=0; i<SYMBOLS; i++) {
printf("%d", symbols.at(i));
}
printf("\n", result);
#endif
} }
mutable bool bcderr; mutable bool bcderr;

124
tests.cpp Normal file
View file

@ -0,0 +1,124 @@
// SPDX-FileCopyrightText: 2021 Jeff Epler
//
// SPDX-License-Identifier: GPL-3.0-only
#ifndef ARDUINO
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
#include <doctest/doctest.h>
#include "decoder.h"
circular_bit_array<6> cba;
circular_symbol_array<6, 4> csa;
TEST_CASE("test bit array") {
for (int i = 0; i < 6; i++)
cba.put(0);
// The next puts need to read out six zeros
for (int i = 0; i < 6; i++)
CHECK(cba.put(1) == 0);
// The next puts need to read out six ones
for (int i = 0; i < 6; i++)
CHECK(cba.put(1) == 1);
cba.put(0);
// content now 111110
CHECK(cba.at(0) == 1);
CHECK(cba.at(1) == 1);
CHECK(cba.at(2) == 1);
CHECK(cba.at(3) == 1);
CHECK(cba.at(4) == 1);
CHECK(cba.at(5) == 0);
}
TEST_CASE("test symbol array") {
for (int i = 0; i < 6; i++)
csa.put(0);
// The next puts need to read out six zeros
for (int i = 0; i < 6; i++)
CHECK(csa.put(i) == 0);
// The next puts need to read out the values above
for (int i = 0; i < 6; i++)
CHECK(csa.put(1) == i);
csa.put(0);
csa.put(1);
csa.put(2);
csa.put(3);
csa.put(4);
csa.put(5);
// content now 012345
CHECK(csa.at(0) == 0);
CHECK(csa.at(1) == 1);
CHECK(csa.at(2) == 2);
CHECK(csa.at(3) == 3);
CHECK(csa.at(4) == 4);
CHECK(csa.at(5) == 5);
}
TEST_CASE("test leap second") {
struct wwvb_time ww = {
.yday = 366,
.year = 16,
.hour = 23,
.minute = 59,
.second = 59,
.ls = 1,
.ly = 1,
.dst = 0,
.dut1 = -4,
};
ww.advance_seconds();
CHECK(ww.second == 60);
CHECK(ww.ls);
CHECK(ww.dut1 == -4);
ww.advance_seconds();
CHECK(ww.second == 0);
CHECK(!ww.ls);
CHECK(ww.dut1 == 6);
}
TEST_CASE("test dst next day") {
struct wwvb_time ww = {
.yday = 73,
.year = 21,
.hour = 23,
.minute = 59,
.second = 59,
.ls = 1,
.ly = 1,
.dst = 2,
};
ww.advance_seconds();
CHECK(ww.second == 0);
CHECK(ww.dst == 3);
}
TEST_CASE("test std next day") {
struct wwvb_time ww = {
.yday = 311,
.year = 21,
.hour = 23,
.minute = 59,
.second = 59,
.ls = 1,
.ly = 1,
.dst = 1,
};
ww.advance_seconds();
CHECK(ww.second == 0);
CHECK(ww.dst == 0);
}
#endif