add tests, refine arduino program
This commit is contained in:
parent
d947fcbced
commit
a4e875b7e3
6 changed files with 190 additions and 24 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -5,3 +5,4 @@
|
||||||
*.o
|
*.o
|
||||||
decoder
|
decoder
|
||||||
firmware
|
firmware
|
||||||
|
tests
|
||||||
|
|
|
||||||
13
Makefile
13
Makefile
|
|
@ -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, $^)
|
||||||
|
|
|
||||||
21
cwwvb.ino
21
cwwvb.ino
|
|
@ -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);
|
||||||
|
|
|
||||||
10
decoder.cpp
10
decoder.cpp
|
|
@ -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);
|
||||||
|
|
|
||||||
45
decoder.h
45
decoder.h
|
|
@ -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
124
tests.cpp
Normal 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
|
||||||
Loading…
Reference in a new issue