aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMatthias P. Braendli <matthias.braendli@mpb.li>2015-12-03 19:49:18 +0100
committerMatthias P. Braendli <matthias.braendli@mpb.li>2015-12-03 19:49:18 +0100
commitd9a1643b1ad937259c6ebebd43ce3101c3e2b8f6 (patch)
treefa79f57f1fdd48ef76ba17ba868d153366096220 /src
parent225b3a3742ab60473503fdb707c6fe941a4bb88f (diff)
downloadglutte-o-matic-d9a1643b1ad937259c6ebebd43ce3101c3e2b8f6.tar.gz
glutte-o-matic-d9a1643b1ad937259c6ebebd43ce3101c3e2b8f6.tar.bz2
glutte-o-matic-d9a1643b1ad937259c6ebebd43ce3101c3e2b8f6.zip
Add fsm logic
Diffstat (limited to 'src')
-rw-r--r--src/fsm/src/common.h7
-rw-r--r--src/fsm/src/fsm.c308
-rw-r--r--src/fsm/src/fsm.h14
3 files changed, 323 insertions, 6 deletions
diff --git a/src/fsm/src/common.h b/src/fsm/src/common.h
index 5381be0..45ba854 100644
--- a/src/fsm/src/common.h
+++ b/src/fsm/src/common.h
@@ -27,5 +27,12 @@
#ifndef _COMMON_H_
#define _COMMON_H_
+// Return the current timestamp in milliseconds. Timestamps are monotonic, and not
+// wall clock time.
+uint64_t timestamp_now(void);
+
+// Return either 0 or 1, somewhat randomly
+int random_bool(void);
+
#endif // _COMMON_H_
diff --git a/src/fsm/src/fsm.c b/src/fsm/src/fsm.c
index 6de1fe4..a6d2bcb 100644
--- a/src/fsm/src/fsm.c
+++ b/src/fsm/src/fsm.c
@@ -29,13 +29,315 @@
static struct fsm_input_signals_t fsm_in;
static struct fsm_output_signals_t fsm_out;
-// Keep track of when we last entered a given state
-static uint64_t time_state[_NUM_FSM_STATES];
+static fsm_state_t current_state;
+
+// Keep track of when we last entered a given state, measured
+// in ms using the timestamp_now() function
+static uint64_t timestamp_state[_NUM_FSM_STATES];
void fsm_init() {
memset(&fsm_in, 0, sizeof(fsm_in));
memset(&fsm_out, 0, sizeof(fsm_out));
- memset(time_state, 0, _NUM_FSM_STATES * sizeof(*time_state));
+ memset(timestamp_state, 0, _NUM_FSM_STATES * sizeof(*timestamp_state));
+
+ current_state = FSM_OISIF;
+}
+
+// Calculate the time spent in the current state
+uint64_t fsm_current_state_time_ms(void) {
+ return timestamp_now() - timestamp_state[current_state];
+}
+
+uint64_t fsm_current_state_time_s(void) {
+ return fsm_current_state_time_ms() / 1000;
+}
+
+// Between turns in a QSO, the repeater sends a letter in CW,
+// different messages are possible. They are sorted here from
+// low to high priority.
+const char* letter_all_ok = "k";
+const char* letter_sstv = "s";
+const char* letter_qrp = "g";
+const char* letter_freq_high = "u";
+const char* letter_freq_low = "d";
+const char* letter_swr_high = "r";
+
+const char* fsm_select_letter(void) {
+ if (fsm_in.swr_high) {
+ return letter_swr_high;
+ }
+ else if (fsm_in.discrim_d) {
+ return letter_freq_low;
+ }
+ else if (fsm_in.discrim_u) {
+ return letter_freq_high;
+ }
+ else if (fsm_in.qrp) {
+ return letter_qrp;
+ }
+ else if (fsm_in.sstv_mode) {
+ return letter_sstv;
+ }
+
+ return letter_all_ok;
+}
+
+void fsm_update() {
+
+ fsm_state_t next_state = current_state;
+
+ // Some defaults for the outgoing signals
+ fsm_out.tx_on = 0;
+ fsm_out.modulation = 0;
+ fsm_out.cw_trigger = 0;
+ // other output signals keep their value
+
+ switch (current_state) {
+ case FSM_OISIF:
+ if (fsm_in.tone_1750 && fsm_in.sq) {
+ next_state = FSM_OPEN1;
+ }
+ else if (fsm_in.start_tm) {
+ if (fsm_in.qrp || fsm_in.swr_high) {
+ next_state = FSM_BALISE_SPECIALE;
+ }
+ else {
+ next_state = FSM_BALISE_LONGUE;
+ }
+ }
+ else if (!fsm_in.qrp && fsm_current_state_time_s() > 20 * 60) {
+ next_state = FSM_BALISE_COURTE;
+ }
+ break;
+
+ case FSM_OPEN1:
+ fsm_out.tx_on = 1;
+
+ if (!fsm_in.sq) {
+ next_state = FSM_OPEN2;
+ }
+ break;
+
+ case FSM_OPEN2:
+ fsm_out.tx_on = 1;
+ fsm_out.modulation = 1;
+
+ if (fsm_current_state_time_ms() > 200) {
+ next_state = FSM_LETTRE;
+ }
+ break;
+
+ case FSM_LETTRE:
+ fsm_out.tx_on = 1;
+ fsm_out.modulation = 1;
+ fsm_out.cw_msg = fsm_select_letter();
+ fsm_out.cw_trigger = 1;
+
+ if (fsm_in.cw_done) {
+ next_state = FSM_ECOUTE;
+ }
+ break;
+
+ case FSM_ECOUTE:
+ fsm_out.tx_on = 1;
+ fsm_out.modulation = 1;
+
+ if (fsm_in.sq) {
+ next_state = FSM_QSO;
+ }
+ else if (fsm_current_state_time_s() > 6 &&
+ timestamp_state[FSM_ECOUTE] - timestamp_state[FSM_OPEN2] <
+ 1000ul * 5) {
+ next_state = FSM_ATTENTE;
+ }
+ else if (fsm_current_state_time_s() > 5 &&
+ timestamp_state[FSM_ECOUTE] - timestamp_state[FSM_OPEN2] <
+ 1000ul * 5 * 60) {
+ next_state = FSM_OISIF;
+ }
+ else if (fsm_current_state_time_s() > 5 &&
+ timestamp_state[FSM_ECOUTE] - timestamp_state[FSM_OPEN2] <
+ 1000ul * 10 * 60) {
+ next_state = FSM_TEXTE_73;
+ }
+ else if (fsm_current_state_time_s() > 5 &&
+ timestamp_state[FSM_ECOUTE] - timestamp_state[FSM_OPEN2] <
+ 1000ul * 15 * 60) {
+ next_state = FSM_TEXTE_HB9G;
+ }
+ else if (fsm_current_state_time_s() > 5 &&
+ timestamp_state[FSM_ECOUTE] - timestamp_state[FSM_OPEN2] >=
+ 1000ul * 15 * 60) {
+ next_state = FSM_TEXTE_LONG;
+ }
+ break;
+
+ case FSM_ATTENTE:
+ if (fsm_in.sq) {
+ next_state = FSM_ECOUTE;
+ }
+ else if (fsm_current_state_time_s() > 15) {
+ next_state = FSM_OISIF;
+ }
+ break;
+
+ case FSM_QSO:
+ fsm_out.tx_on = 1;
+ fsm_out.modulation = 1;
+
+ if (!fsm_in.sq) {
+ next_state = FSM_LETTRE;
+ }
+ else if (fsm_current_state_time_s() > 5 * 60) {
+ next_state = FSM_ANTI_BAVARD;
+ }
+ break;
+
+ case FSM_ANTI_BAVARD:
+ fsm_out.tx_on = 1;
+ // No modulation!
+ fsm_out.cw_msg = "hi hi";
+ fsm_out.cw_trigger = 1;
+
+ if (fsm_in.cw_done) {
+ next_state = FSM_BLOQUE;
+ }
+ break;
+
+ case FSM_BLOQUE:
+ if (fsm_current_state_time_s() > 10) {
+ next_state = FSM_OISIF;
+ }
+ break;
+
+ case FSM_TEXTE_73:
+ fsm_out.tx_on = 1;
+ fsm_out.modulation = 1;
+ fsm_out.cw_msg = "73";
+ fsm_out.cw_trigger = 1;
+
+ if (fsm_in.sq) {
+ next_state = FSM_QSO;
+ }
+ else if (fsm_in.cw_done) {
+ next_state = FSM_OISIF;
+ }
+ break;
+
+ case FSM_TEXTE_HB9G:
+ fsm_out.tx_on = 1;
+ fsm_out.modulation = 1;
+ fsm_out.cw_msg = "hb9g";
+ fsm_out.cw_trigger = 1;
+
+ if (fsm_in.sq) {
+ next_state = FSM_QSO;
+ }
+ else if (fsm_in.cw_done) {
+ next_state = FSM_OISIF;
+ }
+ break;
+
+ case FSM_TEXTE_LONG:
+ fsm_out.tx_on = 1;
+ fsm_out.modulation = 1;
+
+ if (random_bool()) {
+ fsm_out.cw_msg = "hb9g 1628m";
+ }
+ else {
+ fsm_out.cw_msg = "hb9g jn36bk";
+ }
+ fsm_out.cw_trigger = 1;
+
+ if (fsm_in.sq) {
+ next_state = FSM_QSO;
+ }
+ else if (fsm_in.cw_done) {
+ next_state = FSM_OISIF;
+ }
+ break;
+
+ case FSM_BALISE_LONGUE:
+ fsm_out.tx_on = 1;
+
+ // TODO transmit humidity
+ // TODO read voltage
+ if (fsm_in.wind_generator_ok) {
+ fsm_out.cw_msg = "hb9g jn36bk 1628m u 10v5 = T 11 73";
+ // = means same voltage as previous
+ // + means higher
+ // - means lower
+ }
+ else {
+ fsm_out.cw_msg = "hb9g jn36bk 1628m u 10v5 = T 11 #";
+ // The # is the SK digraph
+ }
+ fsm_out.cw_trigger = 1;
+
+ if (fsm_in.sq) {
+ next_state = FSM_OPEN2;
+ }
+ else if (fsm_in.cw_done) {
+ next_state = FSM_OISIF;
+ }
+ break;
+
+ case FSM_BALISE_SPECIALE:
+ fsm_out.tx_on = 1;
+ // TODO read voltage
+ if (fsm_in.wind_generator_ok) {
+ fsm_out.cw_msg = "hb9g u 10v5 73";
+ }
+ else {
+ fsm_out.cw_msg = "hb9g u 10v5 #"; // The # is the SK digraph
+ }
+ fsm_out.cw_trigger = 1;
+
+ if (fsm_in.sq) {
+ next_state = FSM_OPEN2;
+ }
+ else if (fsm_in.cw_done) {
+ next_state = FSM_OISIF;
+ }
+ break;
+
+ case FSM_BALISE_COURTE:
+ fsm_out.tx_on = 1;
+
+ {
+ int rand = random_bool() * 2 + random_bool();
+
+ if (rand == 0) {
+ fsm_out.cw_msg = "hb9g";
+ }
+ else if (rand == 1) {
+ fsm_out.cw_msg = "hb9g jn36bk";
+ }
+ else if (rand == 2) {
+ fsm_out.cw_msg = "hb9g 1628m";
+ }
+ else {
+ fsm_out.cw_msg = "hb9g jn36bk 1628m";
+ }
+ }
+ fsm_out.cw_trigger = 1;
+
+ if (fsm_in.sq) {
+ next_state = FSM_OPEN2;
+ }
+ else if (fsm_in.cw_done) {
+ next_state = FSM_OISIF;
+ }
+ break;
+ default:
+ // Should never happen
+ next_state = FSM_OISIF;
+ break;
+ }
+
+ current_state = next_state;
+ timestamp_state[next_state] = timestamp_now();
}
diff --git a/src/fsm/src/fsm.h b/src/fsm/src/fsm.h
index 869c2c1..16e864a 100644
--- a/src/fsm/src/fsm.h
+++ b/src/fsm/src/fsm.h
@@ -26,7 +26,7 @@
#define _FSM_H_
// List of all states the FSM of the relay can be in
-enum fsm_state_t {
+enum fsm_state_e {
FSM_OISIF = 0, // Idle
FSM_OPEN1, // 1750 Hz received and squelch open
FSM_OPEN2, // Squelch closed
@@ -36,14 +36,17 @@ enum fsm_state_t {
FSM_QSO, // QSO ongoing
FSM_ANTI_BAVARD, // QSO too long, cut transmission
FSM_BLOQUE, // Backoff after ANTI_BAVARD
- FSM_LONG_TEXTE, // Transmit long notice after QSO
- FSM_TEXTE_HB9G, // Transmit short notice after QSO
+ FSM_TEXTE_73, // Transmit 73 after QSO
+ FSM_TEXTE_HB9G, // Transmit HB9G after QSO
+ FSM_TEXTE_LONG, // Transmit either HB9G JN36BK or HB9G 1628M after QSO
FSM_BALISE_LONGUE, // Full-length 2-hour beacon
FSM_BALISE_SPECIALE, // 2-hour beacon when in QRP or with high power return mode
FSM_BALISE_COURTE, // Short intermittent beacon
_NUM_FSM_STATES // Dummy state to count the number of states
};
+typedef enum fsm_state_e fsm_state_t;
+
// All signals that the FSM can read, most of them are actually booleans
struct fsm_input_signals_t {
/* Signals coming from repeater electronics */
@@ -57,9 +60,14 @@ struct fsm_input_signals_t {
int wind_generator_ok; // false if the generator is folded out of the wind
int discrim_d; // FM discriminator says RX is too low in frequency
int tone_1750; // Detect 1750Hz tone
+ int sstv_mode; // The 1750Hz filter is disabled, permitting SSTV usage
/* Signals coming from CW generator */
int cw_done; // The CW generator has finished transmitting the message
+
+ /* Signal coming from the standing wave ratio meter */
+ int swr_high; // We see a lot of return power
+
};
// All signals the FSM has to control