diff options
Diffstat (limited to 'src/common/Core')
-rw-r--r-- | src/common/Core/common.h | 3 | ||||
-rw-r--r-- | src/common/Core/fsm.c | 174 | ||||
-rw-r--r-- | src/common/Core/fsm.h | 13 | ||||
-rw-r--r-- | src/common/Core/main.c | 72 | ||||
-rw-r--r-- | src/common/Core/stats.c | 232 | ||||
-rw-r--r-- | src/common/Core/stats.h | 37 |
6 files changed, 457 insertions, 74 deletions
diff --git a/src/common/Core/common.h b/src/common/Core/common.h index 4918a5b..662ecad 100644 --- a/src/common/Core/common.h +++ b/src/common/Core/common.h @@ -32,7 +32,7 @@ #include <time.h> /* Feature defines */ -#define ENABLE_PSK31 0 +#define ENABLE_PSK 1 #define FLOAT_PI 3.1415926535897932384f @@ -66,6 +66,7 @@ int random_bool(void); #define FAULT_SOURCE_TIM6_ISR 8 #define FAULT_SOURCE_ADC2_QUEUE 9 #define FAULT_SOURCE_ADC2_IRQ 10 +#define FAULT_SOURCE_CW_QUEUE 11 void trigger_fault(int source); int find_last_sunday(const struct tm*); diff --git a/src/common/Core/fsm.c b/src/common/Core/fsm.c index 9fe2c4c..4daf568 100644 --- a/src/common/Core/fsm.c +++ b/src/common/Core/fsm.c @@ -1,7 +1,7 @@ /* * The MIT License (MIT) * - * Copyright (c) 2018 Matthias P. Braendli, Maximilien Cuony + * Copyright (c) 2019 Matthias P. Braendli, Maximilien Cuony * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -27,6 +27,7 @@ #include <stdint.h> #include "Core/common.h" #include "Core/fsm.h" +#include "Core/stats.h" #include "GPIO/usart.h" #include "GPIO/temperature.h" #include "GPIO/analog.h" @@ -44,8 +45,18 @@ static uint64_t timestamp_state[_NUM_FSM_STATES]; static int last_supply_voltage_decivolts = 0; -#define CW_MESSAGE_BALISE_LEN 64 -static char cw_message_balise[CW_MESSAGE_BALISE_LEN]; +#define BALISE_MESSAGE_LEN 64 +static char balise_message[BALISE_MESSAGE_LEN]; + +static int balise_message_empty(void) +{ + return balise_message[0] == '\0'; +} + +static void balise_message_clear(void) +{ + balise_message[0] = '\0'; +} // Each 20 minutes, send a SHORT_BEACON @@ -84,6 +95,7 @@ void fsm_init() { current_state = FSM_OISIF; balise_state = BALISE_FSM_EVEN_HOUR; sstv_state = SSTV_FSM_OFF; + balise_message[0] = '\0'; qso_info.qso_occurred = 0; qso_info.qso_start_time = timestamp_now(); @@ -113,6 +125,9 @@ static const char* state_name(fsm_state_t state) { case FSM_TEXTE_HB9G: return "FSM_TEXTE_HB9G"; case FSM_TEXTE_LONG: return "FSM_TEXTE_LONG"; case FSM_BALISE_LONGUE: return "FSM_BALISE_LONGUE"; + case FSM_BALISE_STATS1: return "FSM_BALISE_STATS1"; + case FSM_BALISE_STATS2: return "FSM_BALISE_STATS2"; + case FSM_BALISE_STATS3: return "FSM_BALISE_STATS3"; case FSM_BALISE_SPECIALE: return "FSM_BALISE_SPECIALE"; case FSM_BALISE_COURTE: return "FSM_BALISE_COURTE"; case FSM_BALISE_COURTE_OPEN: return "FSM_BALISE_COURTE_OPEN"; @@ -141,6 +156,9 @@ static fsm_state_t select_grande_balise(void) { if (fsm_in.qrp || fsm_in.swr_high) { return FSM_BALISE_SPECIALE; } + else if (fsm_in.send_stats) { + return FSM_BALISE_STATS1; + } else { return FSM_BALISE_LONGUE; } @@ -188,7 +206,7 @@ void fsm_update() { // Some defaults for the outgoing signals fsm_out.tx_on = 0; fsm_out.modulation = 0; - fsm_out.cw_psk31_trigger = 0; + fsm_out.cw_psk_trigger = 0; fsm_out.cw_dit_duration = 50; fsm_out.msg_frequency = 960; fsm_out.require_tone_detector = 0; @@ -262,9 +280,9 @@ void fsm_update() { // The letter 'G' is a bit different fsm_out.msg_frequency = 696; } - fsm_out.cw_psk31_trigger = 1; + fsm_out.cw_psk_trigger = 1; - if (fsm_in.cw_psk31_done) { + if (fsm_in.cw_psk_done) { next_state = FSM_ECOUTE; } break; @@ -364,9 +382,10 @@ void fsm_update() { // Short post-delay to underscore the fact that // transmission was forcefully cut off. fsm_out.msg = " HI HI "; - fsm_out.cw_psk31_trigger = 1; + fsm_out.cw_psk_trigger = 1; - if (fsm_in.cw_psk31_done) { + if (fsm_in.cw_psk_done) { + stats_anti_bavard_triggered(); next_state = FSM_BLOQUE; } break; @@ -384,13 +403,13 @@ void fsm_update() { fsm_out.msg_frequency = 696; fsm_out.cw_dit_duration = 70; fsm_out.msg = " 73" CW_POSTDELAY; - fsm_out.cw_psk31_trigger = 1; + fsm_out.cw_psk_trigger = 1; if (fsm_in.sq) { next_state = FSM_QSO; qso_info.qso_start_time = timestamp_now(); } - else if (fsm_in.cw_psk31_done) { + else if (fsm_in.cw_psk_done) { next_state = FSM_OISIF; } break; @@ -403,13 +422,13 @@ void fsm_update() { fsm_out.cw_dit_duration = 70; // No need for CW_PREDELAY, since we are already transmitting fsm_out.msg = " HB9G" CW_POSTDELAY; - fsm_out.cw_psk31_trigger = 1; + fsm_out.cw_psk_trigger = 1; if (fsm_in.sq) { next_state = FSM_QSO; qso_info.qso_start_time = timestamp_now(); } - else if (fsm_in.cw_psk31_done) { + else if (fsm_in.cw_psk_done) { next_state = FSM_OISIF; } break; @@ -429,28 +448,33 @@ void fsm_update() { else { fsm_out.msg = " HB9G JN36BK" CW_POSTDELAY; } - fsm_out.cw_psk31_trigger = 1; + fsm_out.cw_psk_trigger = 1; if (fsm_in.sq) { next_state = FSM_QSO; qso_info.qso_start_time = timestamp_now(); } - else if (fsm_in.cw_psk31_done) { + else if (fsm_in.cw_psk_done) { next_state = FSM_OISIF; } break; case FSM_BALISE_LONGUE: + case FSM_BALISE_STATS1: fsm_out.tx_on = 1; fsm_out.msg_frequency = 588; - fsm_out.cw_dit_duration = 110; +#warning "dit duration = 110" + fsm_out.cw_dit_duration = 30; { const float supply_voltage = round_float_to_half_steps(analog_measure_12v()); const int supply_decivolts = supply_voltage * 10.0f; - char *eol_info = "73"; - if (!fsm_in.wind_generator_ok) { + const char *eol_info = "73"; + if (current_state == FSM_BALISE_STATS1) { + eol_info = "PSK125"; + } + else if (!fsm_in.wind_generator_ok) { eol_info = "\\"; // The backslash is the SK digraph } @@ -466,32 +490,87 @@ void fsm_update() { supply_trend = '-'; } - if (temperature_valid()) { - snprintf(cw_message_balise, CW_MESSAGE_BALISE_LEN-1, - CW_PREDELAY "HB9G JN36BK 1628M U %dV%01d %c T %d %s" CW_POSTDELAY, - supply_decivolts / 10, - supply_decivolts % 10, - supply_trend, - (int)(round_float_to_half_steps(temperature_get())), - eol_info); + if (balise_message_empty()) { +#warning "only for debug" + if (current_state == FSM_BALISE_STATS1) { + snprintf(balise_message, BALISE_MESSAGE_LEN-1, + CW_PREDELAY "HB9G " CW_POSTDELAY); + } + else + if (temperature_valid()) { + snprintf(balise_message, BALISE_MESSAGE_LEN-1, + CW_PREDELAY "HB9G JN36BK 1628M U %dV%01d %c T %d %s" CW_POSTDELAY, + supply_decivolts / 10, + supply_decivolts % 10, + supply_trend, + (int)(round_float_to_half_steps(temperature_get())), + eol_info); + } + else { + snprintf(balise_message, BALISE_MESSAGE_LEN-1, + CW_PREDELAY "HB9G JN36BK 1628M U %dV%01d %c %s" CW_POSTDELAY, + supply_decivolts / 10, + supply_decivolts % 10, + supply_trend, + eol_info); + } + } + + fsm_out.msg = balise_message; + + last_supply_voltage_decivolts = supply_decivolts; + + fsm_out.cw_psk_trigger = 1; + } + + if (fsm_in.cw_psk_done) { + balise_message_clear(); + fsm_out.cw_psk_trigger = 0; + if (current_state == FSM_BALISE_STATS1) { + next_state = FSM_BALISE_STATS2; } else { - snprintf(cw_message_balise, CW_MESSAGE_BALISE_LEN-1, - CW_PREDELAY "HB9G JN36BK 1628M U %dV%01d %c %s" CW_POSTDELAY, - supply_decivolts / 10, - supply_decivolts % 10, - supply_trend, - eol_info); + next_state = FSM_OISIF; } + } + break; + + case FSM_BALISE_STATS2: + fsm_out.tx_on = 1; + fsm_out.msg_frequency = 588; + fsm_out.cw_dit_duration = -3; // PSK125 - fsm_out.msg = cw_message_balise; + fsm_out.msg = stats_build_text(); + fsm_out.cw_psk_trigger = 1; - last_supply_voltage_decivolts = supply_decivolts; + if (fsm_in.cw_psk_done) { + fsm_out.cw_psk_trigger = 0; + next_state = FSM_BALISE_STATS3; + } + break; - fsm_out.cw_psk31_trigger = 1; + case FSM_BALISE_STATS3: + fsm_out.tx_on = 1; + fsm_out.msg_frequency = 588; + fsm_out.cw_dit_duration = 110; + + if (balise_message_empty()) { + const char *eol_info = "73"; + if (!fsm_in.wind_generator_ok) { + eol_info = "\\"; + // The backslash is the SK digraph + } + snprintf(balise_message, BALISE_MESSAGE_LEN-1, + CW_PREDELAY "%s" CW_POSTDELAY, + eol_info); + fsm_out.msg = balise_message; + fsm_out.cw_psk_trigger = 1; } - if (fsm_in.cw_psk31_done) { + if (fsm_in.cw_psk_done) { + stats_beacon_sent(); + fsm_out.cw_psk_trigger = 1; + balise_message_clear(); next_state = FSM_OISIF; } break; @@ -501,28 +580,30 @@ void fsm_update() { fsm_out.msg_frequency = 696; fsm_out.cw_dit_duration = 70; - { + if (balise_message_empty()) { const float supply_voltage = round_float_to_half_steps(analog_measure_12v()); const int supply_decivolts = supply_voltage * 10.0f; - char *eol_info = "73"; + const char *eol_info = "73"; if (!fsm_in.wind_generator_ok) { eol_info = "\\"; // The backslash is the SK digraph } - snprintf(cw_message_balise, CW_MESSAGE_BALISE_LEN-1, + snprintf(balise_message, BALISE_MESSAGE_LEN-1, CW_PREDELAY "HB9G U %dV%01d %s" CW_POSTDELAY, supply_decivolts / 10, supply_decivolts % 10, eol_info); - fsm_out.msg = cw_message_balise; + fsm_out.msg = balise_message; - fsm_out.cw_psk31_trigger = 1; + fsm_out.cw_psk_trigger = 1; } - if (fsm_in.cw_psk31_done) { + if (fsm_in.cw_psk_done) { + stats_beacon_sent(); + balise_message_clear(); next_state = FSM_OISIF; } break; @@ -551,14 +632,15 @@ void fsm_update() { fsm_out.msg = CW_PREDELAY "HB9G JN36BK 1628M" CW_POSTDELAY; } } - fsm_out.cw_psk31_trigger = 1; + fsm_out.cw_psk_trigger = 1; if (current_state == FSM_BALISE_COURTE) { - if (fsm_in.cw_psk31_done) { + if (fsm_in.cw_psk_done) { if (fsm_in.sq) { next_state = FSM_OPEN2; } else { + stats_beacon_sent(); next_state = FSM_OISIF; } } @@ -567,7 +649,8 @@ void fsm_update() { } } else { //FSM_BALISE_COURTE_OPEN - if (fsm_in.cw_psk31_done) { + if (fsm_in.cw_psk_done) { + stats_beacon_sent(); next_state = FSM_OPEN2; } } @@ -626,7 +709,8 @@ void fsm_balise_update() { break; case BALISE_FSM_PENDING: if (current_state == FSM_BALISE_SPECIALE || - current_state == FSM_BALISE_LONGUE) { + current_state == FSM_BALISE_LONGUE || + current_state == FSM_BALISE_STATS3) { next_state = BALISE_FSM_EVEN_HOUR; } break; diff --git a/src/common/Core/fsm.h b/src/common/Core/fsm.h index 37ca386..ba723b1 100644 --- a/src/common/Core/fsm.h +++ b/src/common/Core/fsm.h @@ -1,7 +1,7 @@ /* * The MIT License (MIT) * - * Copyright (c) 2016 Matthias P. Braendli, Maximilien Cuony + * Copyright (c) 2019 Matthias P. Braendli, Maximilien Cuony * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -40,6 +40,9 @@ enum fsm_state_e { 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_STATS1, // Full-length 2-hour beacon at 22:00, 1st part in CW + FSM_BALISE_STATS2, // Full-length 2-hour beacon at 22:00, 2nd part in PSK + FSM_BALISE_STATS3, // Full-length 2-hour beacon at 22:00, 3nd part in CW FSM_BALISE_SPECIALE, // 2-hour beacon when in QRP or with high power return mode FSM_BALISE_COURTE, // Short intermittent beacon FSM_BALISE_COURTE_OPEN, // Short intermittent beacon, need to switch to OPEN @@ -74,6 +77,7 @@ struct fsm_input_signals_t { int discrim_u; // FM discriminator says RX is too high in frequency int qrp; // The relay is currently running with low power int hour_is_even; // 1 if hour is even + int send_stats; // 1 if the balise should contain stats float temp; // temperature in degrees C float humidity; // relative humidity, range [0-100] % int wind_generator_ok; // false if the generator is folded out of the wind @@ -86,7 +90,7 @@ struct fsm_input_signals_t { int long_1750; // 1750Hz detected for more than 5s /* Signals coming from CW and PSK generator */ - int cw_psk31_done; // The CW and PSK generator has finished transmitting the message + int cw_psk_done; // The CW and PSK generator has finished transmitting the message /* Signal coming from the standing wave ratio meter */ int swr_high; // We see a lot of return power @@ -102,9 +106,8 @@ struct fsm_output_signals_t { /* Signals to the CW and PSK generator */ const char* msg; // The message to transmit int msg_frequency; // What audio frequency for the CW or PSK message - int cw_dit_duration; // CW speed, dit duration in ms - int cw_psk31_trigger; // Set to true to trigger a CW or PSK31 transmission. - // PSK31 is sent if cw_dit_duration is 0 + int cw_dit_duration; // CW speed, dit duration in ms or PSK speed (see enum cw_psk_types_e) + int cw_psk_trigger; // Set to true to trigger a CW or PSK transmission. /* Tone detector */ int require_tone_detector; // Enables audio input and detector for DTMF and 1750 diff --git a/src/common/Core/main.c b/src/common/Core/main.c index 07657fa..b43463a 100644 --- a/src/common/Core/main.c +++ b/src/common/Core/main.c @@ -42,6 +42,7 @@ #include "GPIO/i2c.h" #include "GPS/gps.h" #include "Core/fsm.h" +#include "Core/stats.h" #include "Core/common.h" #include "GPIO/usart.h" #include "Core/delay.h" @@ -140,7 +141,7 @@ int main(void) { static void launcher_task(void __attribute__ ((unused))*pvParameters) { usart_debug_puts("CW init\r\n"); - cw_psk31_init(16000); + cw_psk_init(16000); usart_debug_puts("PIO init\r\n"); pio_init(); @@ -356,7 +357,7 @@ static void audio_callback(void __attribute__ ((unused))*context, int select_buf leds_turn_on(LED_RED); } - size_t samples_len = cw_psk31_fill_buffer(samples, AUDIO_BUF_LEN); + size_t samples_len = cw_psk_fill_buffer(samples, AUDIO_BUF_LEN); if (samples_len == 0) { for (int i = 0; i < AUDIO_BUF_LEN; i++) { @@ -408,15 +409,6 @@ static void gps_monit_task(void __attribute__ ((unused))*pvParameters) { while (1) { const uint64_t now = timestamp_now(); - if (last_volt_and_temp_timestamp + 20000 < now) { - usart_debug("ALIM %d mV\r\n", (int)roundf(1000.0f * analog_measure_12v())); - - const float temp = temperature_get(); - usart_debug("TEMP %d.%02d\r\n", (int)temp, (int)(temp * 100.0f - (int)(temp) * 100.0f)); - - last_volt_and_temp_timestamp = now; - } - struct tm time = {0}; int time_valid = local_time(&time); int derived_mode = 0; @@ -463,6 +455,22 @@ static void gps_monit_task(void __attribute__ ((unused))*pvParameters) { last_hour_is_even_change_timestamp = now; } + if (last_volt_and_temp_timestamp + 20000 < now) { + const float u_bat = analog_measure_12v(); + usart_debug("ALIM %d mV\r\n", (int)roundf(1000.0f * u_bat)); + + stats_voltage(u_bat); + if (time_valid && time.tm_min == 0) { + stats_voltage_at_full_hour(time.tm_hour, u_bat); + } + + const float temp = temperature_get(); + stats_temp(temp); + usart_debug("TEMP %d.%02d\r\n", (int)temp, (int)(temp * 100.0f - (int)(temp) * 100.0f)); + + last_volt_and_temp_timestamp = now; + } + int num_sv_used = 0; gps_utctime(&gps_time, &num_sv_used); @@ -531,6 +539,7 @@ static void exercise_fsm(void __attribute__ ((unused))*pvParameters) int cw_last_trigger = 0; int last_tm_trigger_button = 0; + int last_tx_on = 0; int last_sq = 0; int last_qrp = 0; int last_cw_done = 0; @@ -567,30 +576,26 @@ static void exercise_fsm(void __attribute__ ((unused))*pvParameters) } if (last_wind_generator_ok != fsm_input.wind_generator_ok) { last_wind_generator_ok = fsm_input.wind_generator_ok; + stats_wind_generator_moved(); usart_debug("In eolienne %s\r\n", last_wind_generator_ok ? "vent" : "replie"); } - if (tm_trigger_button == 1 && last_tm_trigger_button == 0) { - fsm_balise_force(); - } - last_tm_trigger_button = tm_trigger_button; - - const int cw_psk31_done = !cw_psk31_busy(); - const int cw_done = cw_psk31_done && only_zero_in_audio_buffer; + const int cw_psk_done = !cw_psk_busy(); + const int cw_done = cw_psk_done && only_zero_in_audio_buffer; // Set the done flag to 1 only once, when cw_done switches from 0 to 1 if (last_cw_done != cw_done) { usart_debug("In cw_done change %d %d\r\n", cw_done, only_zero_in_audio_buffer); if (cw_done) { - fsm_input.cw_psk31_done = cw_done; + fsm_input.cw_psk_done = cw_done; leds_turn_off(LED_ORANGE); } last_cw_done = cw_done; } else { - fsm_input.cw_psk31_done = 0; + fsm_input.cw_psk_done = 0; } @@ -610,7 +615,21 @@ static void exercise_fsm(void __attribute__ ((unused))*pvParameters) fsm_input.swr_high = swr_error_flag; fsm_input.hour_is_even = hour_is_even; + struct tm time = {0}; + int time_valid = local_time(&time); + // TODO use derived too? + if (time_valid) { + fsm_input.send_stats = (time.tm_hour == 22) ? 1 : 0; + } + fsm_update_inputs(&fsm_input); + + if (tm_trigger_button == 1 && last_tm_trigger_button == 0) { + fsm_update_inputs(&fsm_input); + fsm_balise_force(); + } + last_tm_trigger_button = tm_trigger_button; + fsm_update(); fsm_balise_update(); const int disable_1750_filter = fsm_sstv_update(); @@ -620,15 +639,22 @@ static void exercise_fsm(void __attribute__ ((unused))*pvParameters) fsm_get_outputs(&fsm_out); pio_set_tx(fsm_out.tx_on); + if (fsm_out.tx_on != last_tx_on) { + stats_tx_switched(); + last_tx_on = fsm_out.tx_on; + } pio_set_mod_off(!fsm_out.modulation); // Add message to CW generator only on rising edge of trigger - if (fsm_out.cw_psk31_trigger && !cw_last_trigger) { - cw_psk31_push_message(fsm_out.msg, fsm_out.cw_dit_duration, fsm_out.msg_frequency); + if (fsm_out.cw_psk_trigger && !cw_last_trigger) { + const int success = cw_psk_push_message(fsm_out.msg, fsm_out.cw_dit_duration, fsm_out.msg_frequency); + if (!success) { + usart_debug_puts("cw_psk_push_message failed"); + } leds_turn_on(LED_ORANGE); } - cw_last_trigger = fsm_out.cw_psk31_trigger; + cw_last_trigger = fsm_out.cw_psk_trigger; } } diff --git a/src/common/Core/stats.c b/src/common/Core/stats.c new file mode 100644 index 0000000..e2ec937 --- /dev/null +++ b/src/common/Core/stats.c @@ -0,0 +1,232 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Matthias P. Braendli, Maximilien Cuony + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <string.h> +#include <stdio.h> +#include <stdint.h> +#include "Core/stats.h" +#include "Core/common.h" +#include "vc.h" + +static int values_valid = 0; +static uint32_t num_beacons_sent = 0; +static uint32_t num_wind_generator_movements = 0; +static uint32_t num_tx_switch = 0; +static uint32_t num_antibavard = 0; +static float battery_min = -1.0f; +static float battery_max = -1.0f; +static float battery_per_hour[24]; +static float temp_min = -1.0f; +static float temp_max = -1.0f; + +/* Ideas + * + * Version + * Uptime + * Number of beacons + * Ubat min/max/avg + * Temperature min/max/avg + * QRP/QRO time ratio in % + * Number of K, G, D, U, S, R sent + * Number of TX On/Off + * How many times anti-bavard got triggered + * Max SWR ratio + * Number of wind generator movements + * Longest QSO duration + * Number of GNSS SVs tracked + */ + +#define STATS_LEN 1024 // also check MAX_MESSAGE_LEN in cw.c +static char stats_text[STATS_LEN]; +static int32_t stats_end_ix = 0; + +static void clear_stats() +{ + num_beacons_sent = 0; + num_wind_generator_movements = 0; + num_tx_switch = 0; + num_antibavard = 0; + battery_min = -1.0f; + battery_max = -1.0f; + temp_min = -1.0f; + temp_max = -1.0f; + for (int i = 0; i < 24; i++) { + battery_per_hour[i] = -1.0f; + } + values_valid = 1; +} + +void stats_voltage_at_full_hour(int hour, float u_bat) +{ + if (values_valid == 0) { + clear_stats(); + } + + if (hour > 0 && hour < 24) { + battery_per_hour[hour] = u_bat; + } +} + +void stats_voltage(float u_bat) +{ + if (values_valid == 0) { + clear_stats(); + } + + if (u_bat < battery_min || battery_min == -1.0f) { + battery_min = u_bat; + } + + if (u_bat > battery_max || battery_max == -1.0f) { + battery_max = u_bat; + } +} + +void stats_temp(float temp) +{ + if (values_valid == 0) { + clear_stats(); + } + + if (temp < temp_min || temp_min == -1.0f) { + temp_min = temp; + } + + if (temp > temp_max || temp_max == -1.0f) { + temp_max = temp; + } +} + +void stats_wind_generator_moved() +{ + if (values_valid == 0) { + clear_stats(); + } + + num_wind_generator_movements++; +} + +void stats_beacon_sent() +{ + if (values_valid == 0) { + clear_stats(); + } + + num_beacons_sent++; +} + +void stats_tx_switched() +{ + if (values_valid == 0) { + clear_stats(); + } + + num_tx_switch++; +} + +void stats_anti_bavard_triggered() +{ + if (values_valid == 0) { + clear_stats(); + } + + num_antibavard++; +} + +const char* stats_build_text(void) +{ + struct tm time = {0}; + int time_valid = local_time(&time); + + uint64_t uptime = timestamp_now(); + uint32_t uptime_j = uptime / (24 * 3600 * 1000); + uptime -= uptime_j * (24 * 3600 * 1000); + uint32_t uptime_h = uptime / (3600 * 1000); + uptime -= uptime_h * (24 * 3600 * 1000); + uint32_t uptime_m = uptime / (60 * 1000); + + stats_end_ix = snprintf(stats_text + stats_end_ix, STATS_LEN - 1 - stats_end_ix, + "HB9G www.glutte.ch HB9G www.glutte.ch\n"); + + if (time_valid) { + stats_end_ix += snprintf(stats_text + stats_end_ix, STATS_LEN - 1 - stats_end_ix, + "Statistiques du %04d-%02d-%02d\n", + time.tm_year + 1900, time.tm_mon + 1, time.tm_mday); + } + else { + stats_end_ix += snprintf(stats_text + stats_end_ix, STATS_LEN - 1 - stats_end_ix, + "Statistiques de la journee\n"); + } + + const int battery_min_decivolt = 10.0f * battery_min; + const int battery_max_decivolt = 10.0f * battery_max; + + stats_end_ix += snprintf(stats_text + stats_end_ix, STATS_LEN - 1 - stats_end_ix, + "Version= %s\n" + "Uptime= %dj%dh%dm\n" + "U min,max= %dV%01d,%dV%01d\n" , + vc_get_version(), + uptime_j, uptime_h, uptime_m, + battery_min_decivolt / 10, battery_min_decivolt % 10, + battery_max_decivolt / 10, battery_max_decivolt % 10); + + stats_end_ix += snprintf(stats_text + stats_end_ix, STATS_LEN - 1 - stats_end_ix, + "U heures pleines=\n"); + + for (int h = 0; h < 24; h++) { + if (battery_per_hour[h] == -1.0f) { + stats_end_ix += snprintf(stats_text + stats_end_ix, STATS_LEN - 1 - stats_end_ix, + " ? "); + } + else { + const int battery_decivolts = 10.0f * battery_per_hour[h]; + stats_end_ix += snprintf(stats_text + stats_end_ix, STATS_LEN - 1 - stats_end_ix, + " %dV%01d", + battery_decivolts / 10, battery_decivolts % 10); + } + } + stats_end_ix += snprintf(stats_text + stats_end_ix, STATS_LEN - 1 - stats_end_ix, + "\n"); + + const int temp_min_decidegree = 10.0f * temp_min; + const int temp_max_decidegree = 10.0f * temp_max; + + stats_end_ix += snprintf(stats_text + stats_end_ix, STATS_LEN - 1 - stats_end_ix, + "Nbre de commutations eolienne= %d\n" + "Temp min,max= %dC%01d,%dC%01d\n" + "Nbre de balises= %d\n" + "Nbre de TX ON/OFF= %d\n" + "Nbre anti-bavard= %d\n", + num_wind_generator_movements, + temp_min_decidegree / 10, temp_min_decidegree % 10, + temp_max_decidegree / 10, temp_max_decidegree % 10, + num_beacons_sent, + num_tx_switch, + num_antibavard + ); + + values_valid = 0; + + return stats_text; +} diff --git a/src/common/Core/stats.h b/src/common/Core/stats.h new file mode 100644 index 0000000..06e1515 --- /dev/null +++ b/src/common/Core/stats.h @@ -0,0 +1,37 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Matthias P. Braendli, Maximilien Cuony + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once +#include <stdint.h> + +void stats_voltage_at_full_hour(int hour, float u_bat); +void stats_voltage(float u_bat); + +void stats_temp(float temp); +void stats_wind_generator_moved(void); +void stats_beacon_sent(void); +void stats_tx_switched(void); +void stats_anti_bavard_triggered(void); + +const char* stats_build_text(void); |