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
|
||||
decoder
|
||||
firmware
|
||||
tests
|
||||
|
|
|
|||
13
Makefile
13
Makefile
|
|
@ -3,7 +3,7 @@
|
|||
# SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
FIRMWARE = firmware/cwwvb.ino.elf
|
||||
all: decoder $(FIRMWARE)
|
||||
all: decoder $(FIRMWARE) run-tests
|
||||
|
||||
decoder: decoder.cpp Makefile decoder.h
|
||||
g++ -Wall -g -Og -o $@ $< -DMAIN
|
||||
|
|
@ -18,3 +18,14 @@ PORT := /dev/ttyACM0
|
|||
.PHONY: flash
|
||||
flash: $(FIRMWARE)
|
||||
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 tick_subsec;
|
||||
|
||||
WWVBDecoder<> dec;
|
||||
constexpr int CENTRAL_COUNT = 3000000 / dec.SUBSEC;
|
||||
static_assert(CENTRAL_COUNT <= 65535);
|
||||
|
|
@ -75,7 +77,7 @@ void TimerHandler0(void) {
|
|||
digitalWrite(PIN_MON, LOW);
|
||||
TC3->COUNT16.CC[0].reg = cc;
|
||||
#if MONITOR_LL
|
||||
putc(i ? '#' : '_');
|
||||
putc(i ? '_' : '#', stderr);
|
||||
#endif
|
||||
if (introduced_error.load()) {
|
||||
introduced_error.fetch_sub(1);
|
||||
|
|
@ -84,7 +86,8 @@ void TimerHandler0(void) {
|
|||
if (dec.update(i)) {
|
||||
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) {
|
||||
wq.put(tick);
|
||||
}
|
||||
|
|
@ -102,6 +105,8 @@ void set_tc(int n, bool hold) {
|
|||
if (n < -(int)CENTRAL_COUNT / 100)
|
||||
n = -(int)CENTRAL_COUNT / 100;
|
||||
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,
|
||||
ss_P, hold ? "HOLD" : "INTG");
|
||||
}
|
||||
|
|
@ -158,6 +163,7 @@ void loop() {
|
|||
|
||||
digitalWrite(PIN_LED, HIGH);
|
||||
wq.take()();
|
||||
fflush(stdout);
|
||||
digitalWrite(PIN_LED, LOW);
|
||||
}
|
||||
|
||||
|
|
@ -223,7 +229,8 @@ void try_decode() {
|
|||
{
|
||||
char buf[snapshot.SYMBOLS];
|
||||
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);
|
||||
printf("%.*s health=%3d%%", sizeof(buf), buf,
|
||||
|
|
@ -232,19 +239,19 @@ void try_decode() {
|
|||
|
||||
if (snapshot.symbols.at(snapshot.SYMBOLS - 1) == 2) {
|
||||
if (snapshot.decode_minute(w)) {
|
||||
tick_subsec = mod_diff<snapshot.SUBSEC>(snapshot.sos, 5);
|
||||
w.advance_minutes();
|
||||
ever_set = true;
|
||||
display_time();
|
||||
}
|
||||
}
|
||||
|
||||
if (ever_set) {
|
||||
tick();
|
||||
}
|
||||
}
|
||||
|
||||
void tick() {
|
||||
// if (ever_set) {
|
||||
w.advance_seconds();
|
||||
display_time();
|
||||
//}
|
||||
}
|
||||
|
||||
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
|
||||
void wwvb_time::advance_minutes(int n) {
|
||||
if (seconds_in_minute() == 61) {
|
||||
if (seconds_in_minute() != 60) {
|
||||
ls = 0;
|
||||
if (dut1 < 0)
|
||||
dut1 += 10;
|
||||
else if (dut1 > 0)
|
||||
dut1 -= 10;
|
||||
}
|
||||
|
||||
second = 0;
|
||||
|
|
@ -122,7 +126,7 @@ void wwvb_time::advance_minutes(int n) {
|
|||
yday++;
|
||||
if (dst == 1)
|
||||
dst = 0;
|
||||
if (dst == 2)
|
||||
else if (dst == 2)
|
||||
dst = 3;
|
||||
if (yday < last_yday(year))
|
||||
return;
|
||||
|
|
@ -137,7 +141,7 @@ void wwvb_time::advance_minutes(int n) {
|
|||
using namespace std;
|
||||
|
||||
int main() {
|
||||
WWVBDecoder<120> dec;
|
||||
WWVBDecoder<> dec;
|
||||
|
||||
static char zone[] = "TZ=UTC";
|
||||
putenv(zone);
|
||||
|
|
|
|||
45
decoder.h
45
decoder.h
|
|
@ -72,7 +72,7 @@ template <int N, int M> struct circular_symbol_array {
|
|||
return result;
|
||||
}
|
||||
|
||||
bool put(int v) {
|
||||
int put(int v) {
|
||||
int result = 0;
|
||||
for (int j = 0; j < M; j++) {
|
||||
result = (result << 1) | data.put(v & (1 << (M - 1)));
|
||||
|
|
@ -89,7 +89,6 @@ struct wwvb_time {
|
|||
int16_t yday;
|
||||
int8_t year, hour, minute, second;
|
||||
int8_t ls, ly, dst, dut1;
|
||||
int8_t month, mday;
|
||||
|
||||
time_t to_utc() 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;
|
||||
}
|
||||
|
||||
// We're informed that a new second _just started_, so
|
||||
// signal.at(BUFFER-1) is the first sample of the new second. and
|
||||
// signal.at(BUFFER-SUBSEC-1) is the first sample of the previous second
|
||||
// A second just concluded, so signal.at(BUFFER-1) is the last sample of
|
||||
// the second, and signal.at(BUFFER-SUBSEC) is the first sample of the
|
||||
// second
|
||||
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_b = count(OFFSET + p1, OFFSET + p2);
|
||||
int count_c = count(OFFSET + p2, OFFSET + p3);
|
||||
|
|
@ -246,16 +250,23 @@ struct WWVBDecoder {
|
|||
|
||||
int result = 0;
|
||||
|
||||
if (count_c > lc / 2)
|
||||
result = 2;
|
||||
else if (count_b > lb / 2)
|
||||
if (count_c > lc / 2) {
|
||||
if (count_b > lb / 2) {
|
||||
result = 2;
|
||||
} else {
|
||||
result = 3; // a nonsense symbol
|
||||
}
|
||||
} else if (count_b > lb / 2) {
|
||||
result = 1;
|
||||
}
|
||||
|
||||
int h = 0;
|
||||
h += check_health(count_a, la, 1);
|
||||
h += check_health(count_b, lb, result != 0);
|
||||
h += check_health(count_c, lc, result == 2);
|
||||
h += check_health(count_d, ld, 0);
|
||||
if (result != 3) {
|
||||
h += check_health(count_a, la, 1);
|
||||
h += check_health(count_b, lb, result != 0);
|
||||
h += check_health(count_c, lc, result == 2);
|
||||
h += check_health(count_d, ld, 0);
|
||||
}
|
||||
|
||||
int sc = symbol_count++;
|
||||
int si = sc % SYMBOLS;
|
||||
|
|
@ -264,6 +275,14 @@ struct WWVBDecoder {
|
|||
health += (h - oh);
|
||||
|
||||
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;
|
||||
|
|
|
|||
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