From 42ca19997b8a6777702db5ae39c1aa7581a9787f Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Mon, 1 Jul 2019 23:02:21 +0200 Subject: Add stats beacon - Add PSK63 and PSK125 functionality - Rename cw_psk31 to cw_psk - Add a new type of long beacon, transmitted at 22:00 --- src/common/Audio/cw.c | 155 ++++++++++++++++--------------- src/common/Audio/cw.h | 36 ++++--- src/common/Core/common.h | 3 +- src/common/Core/fsm.c | 174 +++++++++++++++++++++++++--------- src/common/Core/fsm.h | 13 ++- src/common/Core/main.c | 72 +++++++++----- src/common/Core/stats.c | 232 ++++++++++++++++++++++++++++++++++++++++++++++ src/common/Core/stats.h | 37 ++++++++ src/common/sourcelist.txt | 1 + 9 files changed, 561 insertions(+), 162 deletions(-) create mode 100644 src/common/Core/stats.c create mode 100644 src/common/Core/stats.h diff --git a/src/common/Audio/cw.c b/src/common/Audio/cw.c index 667d459..e7cc806 100644 --- a/src/common/Audio/cw.c +++ b/src/common/Audio/cw.c @@ -22,15 +22,15 @@ * SOFTWARE. */ -/* CW and PSK31 generator +/* CW and BPSK{31,63,125} generator * * Concept: * * +-------------------+ +----------------+ - * | cw_push_message() | -> cw_msg_queue -> | cw_psk31task() | -> cw_audio_queue + * | cw_push_message() | -> cw_msg_queue -> | cw_psktask() | -> cw_audio_queue * +-------------------+ +----------------+ * - * The cw_psk31_fill_buffer() function can be called to fetch audio from the + * The cw_psk_fill_buffer() function can be called to fetch audio from the * audio_queue */ @@ -119,12 +119,12 @@ const uint8_t cw_mapping[60] = { // {{{ 0b1010111, // SK , ASCII '\' }; //}}} -#if ENABLE_PSK31 +#if ENABLE_PSK /* - * PSK31 Varicode + * PSK Varicode * http://aintel.bi.ehu.es/psk31.html */ -const char *psk31_varicode[] = { // {{{ +const char *psk_varicode[] = { // {{{ "1010101011", "1011011011", "1011101101", @@ -265,7 +265,7 @@ struct cw_message_s { int freq; - // If dit_duration is 0, the message is sent in PSK31 + // If dit_duration is negative, the message is sent in PSK int dit_duration; }; @@ -274,15 +274,15 @@ QueueHandle_t cw_msg_queue; // Queue that contains audio data QueueHandle_t cw_audio_queue; -static int cw_psk31_samplerate; +static int cw_psk_samplerate; static int cw_transmit_ongoing; -static void cw_psk31_task(void *pvParameters); +static void cw_psk_task(void *pvParameters); -void cw_psk31_init(unsigned int samplerate) +void cw_psk_init(unsigned int samplerate) { - cw_psk31_samplerate = samplerate; + cw_psk_samplerate = samplerate; cw_transmit_ongoing = 0; cw_msg_queue = xQueueCreate(10, sizeof(struct cw_message_s)); @@ -296,7 +296,7 @@ void cw_psk31_init(unsigned int samplerate) } xTaskCreate( - cw_psk31_task, + cw_psk_task, "CWPSKTask", 8*configMINIMAL_STACK_SIZE, (void*) NULL, @@ -349,12 +349,7 @@ size_t cw_symbol(uint8_t sym, uint8_t *on_buffer, size_t on_buffer_size) return pos; } -// Transmit a string in morse code or PSK31. -// Supported range for CW: -// All ASCII between '+' and '\', which includes -// numerals and capital letters. -// Distinction between CW and PSK31 is done on dit_duration==0 -int cw_psk31_push_message(const char* text, int dit_duration, int frequency) +int cw_psk_push_message(const char* text, int dit_duration, int frequency) { const int text_len = strlen(text); @@ -375,7 +370,9 @@ int cw_psk31_push_message(const char* text, int dit_duration, int frequency) msg.freq = frequency; msg.dit_duration = dit_duration; - xQueueSendToBack(cw_msg_queue, &msg, portMAX_DELAY); /* Send Message */ + if (xQueueSendToBack(cw_msg_queue, &msg, portMAX_DELAY) != pdTRUE) { + trigger_fault(FAULT_SOURCE_CW_QUEUE); + } cw_message_sent(msg.message); @@ -407,17 +404,17 @@ static size_t cw_text_to_on_buffer(const char *msg, uint8_t *on_buffer, size_t o } -#if ENABLE_PSK31 +#if ENABLE_PSK /* * Turn a null terminated ASCII string into a uint8_t buffer - * of 0 and 1 representing the PSK31 varicode for the input. + * of 0 and 1 representing the PSK varicode for the input. * * outstr must be at least size 20 + strlen(instr)*12 + 20 to accomodate * the header and tail. * * Returns number of bytes written. */ -static size_t psk31_text_to_phase_buffer(const char* instr, uint8_t* outbits) +static size_t psk_text_to_phase_buffer(const char* instr, uint8_t* outbits) { int i=0, j, k; @@ -428,12 +425,14 @@ static size_t psk31_text_to_phase_buffer(const char* instr, uint8_t* outbits) /* Encode the message, with 00 between letters */ for (j=0; j < strlen(instr); j++) { - const char* varicode_bits = psk31_varicode[(int)instr[j]]; - for(k=0; k < strlen(varicode_bits); k++) { - outbits[i++] = (varicode_bits[k] == '1') ? 1 : 0; + if (instr[j] < sizeof(psk_varicode)) { + const char* varicode_bits = psk_varicode[(int)instr[j]]; + for(k=0; k < strlen(varicode_bits); k++) { + outbits[i++] = (varicode_bits[k] == '1') ? 1 : 0; + } + outbits[i++] = 0; + outbits[i++] = 0; } - outbits[i++] = 0; - outbits[i++] = 0; } /* Tail of 0s */ @@ -446,7 +445,7 @@ static size_t psk31_text_to_phase_buffer(const char* instr, uint8_t* outbits) #endif -size_t cw_psk31_fill_buffer(int16_t *buf, size_t bufsize) +size_t cw_psk_fill_buffer(int16_t *buf, size_t bufsize) { if (xQueueReceiveFromISR(cw_audio_queue, buf, NULL)) { return bufsize; @@ -457,7 +456,7 @@ size_t cw_psk31_fill_buffer(int16_t *buf, size_t bufsize) } static int16_t cw_audio_buf[AUDIO_BUF_LEN]; -static uint8_t cw_psk31_buffer[MAX_ON_BUFFER_LEN]; +static uint8_t cw_psk_buffer[MAX_ON_BUFFER_LEN]; static struct cw_message_s cw_fill_msg_current; // Routine to generate CW audio @@ -467,7 +466,7 @@ static int16_t cw_generate_audio(float omega, int i, int __attribute__ ((unused) { int16_t s = 0; // Remove clicks from CW - if (cw_psk31_buffer[i]) { + if (cw_psk_buffer[i]) { const float remaining = 32768.0f - cw_generate_audio_ampl; cw_generate_audio_ampl += remaining / 64.0f; } @@ -488,82 +487,93 @@ static int16_t cw_generate_audio(float omega, int i, int __attribute__ ((unused) return s; } -#if ENABLE_PSK31 -static float psk31_generate_audio_nco = 0.0f; -static int psk31_current_psk_phase = 1; -static int16_t psk31_generate_audio(float omega, int i, int t, int samples_per_symbol) +#if ENABLE_PSK +static float psk_generate_audio_nco = 0.0f; +static int psk_current_psk_phase = 1; +static int16_t psk_generate_audio(float omega, int i, int t, int samples_per_symbol) { int16_t s = 0; - const float base_ampl = 20000.0f; - float psk31_generate_audio_ampl = 0.0f; + const float base_ampl = 10000.0f; + float psk_generate_audio_ampl = 0.0f; - if (cw_psk31_buffer[i] == 1) { - psk31_generate_audio_ampl = base_ampl; + if (cw_psk_buffer[i] == 1) { + psk_generate_audio_ampl = base_ampl; } else { - psk31_generate_audio_ampl = base_ampl * arm_cos_f32( + psk_generate_audio_ampl = base_ampl * arm_cos_f32( FLOAT_PI*(float)t/(float)samples_per_symbol); } - psk31_generate_audio_nco += omega; - if (psk31_generate_audio_nco > FLOAT_PI) { - psk31_generate_audio_nco -= 2.0f * FLOAT_PI; + psk_generate_audio_nco += omega; + if (psk_generate_audio_nco > FLOAT_PI) { + psk_generate_audio_nco -= 2.0f * FLOAT_PI; } - s = psk31_generate_audio_ampl * - arm_sin_f32(psk31_generate_audio_nco + - (psk31_current_psk_phase == 1 ? 0.0f : FLOAT_PI)); + s = psk_generate_audio_ampl * + arm_sin_f32(psk_generate_audio_nco + + (psk_current_psk_phase == 1 ? 0.0f : FLOAT_PI)); return s; } #endif -static void cw_psk31_task(void __attribute__ ((unused))*pvParameters) +static void cw_psk_task(void __attribute__ ((unused))*pvParameters) { - int buf_pos = 0; + int buf_pos = 0; while (1) { int status = xQueueReceive(cw_msg_queue, &cw_fill_msg_current, portMAX_DELAY); if (status == pdTRUE) { - - size_t cw_psk31_buffer_len = 0; + size_t cw_psk_buffer_len = 0; cw_transmit_ongoing = 1; - if (cw_fill_msg_current.dit_duration) { - cw_psk31_buffer_len = cw_text_to_on_buffer( + if (cw_fill_msg_current.dit_duration == 0) { + // Illegal + cw_transmit_ongoing = 0; + continue; + } + else if (cw_fill_msg_current.dit_duration > 0) { + cw_psk_buffer_len = cw_text_to_on_buffer( cw_fill_msg_current.message, - cw_psk31_buffer, + cw_psk_buffer, MAX_ON_BUFFER_LEN); } -#if ENABLE_PSK31 +#if ENABLE_PSK else { - cw_psk31_buffer_len = psk31_text_to_phase_buffer( + cw_psk_buffer_len = psk_text_to_phase_buffer( cw_fill_msg_current.message, - cw_psk31_buffer); + cw_psk_buffer); } #endif // Angular frequency of NCO const float omega = 2.0f * FLOAT_PI * cw_fill_msg_current.freq / - (float)cw_psk31_samplerate; + (float)cw_psk_samplerate; - const int samples_per_symbol = (cw_fill_msg_current.dit_duration != 0) ? - (cw_psk31_samplerate * cw_fill_msg_current.dit_duration) / 1000 : + const int samples_per_symbol = (cw_fill_msg_current.dit_duration == -1) ? /* BPSK31 is at 31.25 symbols per second. */ - cw_psk31_samplerate * 100 / 3125; - -#if ENABLE_PSK31 - psk31_current_psk_phase = 1; + cw_psk_samplerate * 100 / 3125 : + (cw_fill_msg_current.dit_duration == -2) ? + /* BPSK63 is at 2*31.25 symbols per second. */ + cw_psk_samplerate * 50 / 3125 : + (cw_fill_msg_current.dit_duration == -3) ? + /* BPSK125 is at 4*31.25 symbols per second. */ + cw_psk_samplerate * 25 / 3125 : + /* CW directly depends on dit_duration, which is in ms */ + (cw_psk_samplerate * cw_fill_msg_current.dit_duration) / 1000; + +#if ENABLE_PSK + psk_current_psk_phase = 1; #endif - for (int i = 0; i < cw_psk31_buffer_len; i++) { + for (int i = 0; i < cw_psk_buffer_len; i++) { for (int t = 0; t < samples_per_symbol; t++) { -#if ENABLE_PSK31 - int16_t s = (cw_fill_msg_current.dit_duration != 0) ? +#if ENABLE_PSK + int16_t s = (cw_fill_msg_current.dit_duration > 0) ? cw_generate_audio(omega, i, t) : - psk31_generate_audio(omega, i, t, samples_per_symbol); + psk_generate_audio(omega, i, t, samples_per_symbol); #else int16_t s = cw_generate_audio(omega, i, t); #endif @@ -571,9 +581,9 @@ static void cw_psk31_task(void __attribute__ ((unused))*pvParameters) // Stereo for (int channel = 0; channel < 2; channel++) { if (buf_pos == AUDIO_BUF_LEN) { - // It should take AUDIO_BUF_LEN/cw_psk31_samplerate seconds to send one buffer. + // It should take AUDIO_BUF_LEN/cw_psk_samplerate seconds to send one buffer. // If it takes more than 4 times as long, we think there is a problem. - const TickType_t reasonable_delay = pdMS_TO_TICKS(4000 * AUDIO_BUF_LEN / cw_psk31_samplerate); + const TickType_t reasonable_delay = pdMS_TO_TICKS(4000 * AUDIO_BUF_LEN / cw_psk_samplerate); if (xQueueSendToBack(cw_audio_queue, &cw_audio_buf, reasonable_delay) != pdTRUE) { trigger_fault(FAULT_SOURCE_CW_AUDIO_QUEUE); } @@ -584,21 +594,20 @@ static void cw_psk31_task(void __attribute__ ((unused))*pvParameters) } -#if ENABLE_PSK31 - if (cw_psk31_buffer[i] == 0) { - psk31_current_psk_phase *= -1; +#if ENABLE_PSK + if (cw_psk_buffer[i] == 0) { + psk_current_psk_phase *= -1; } #endif } // We have completed this message - cw_transmit_ongoing = 0; } } } -int cw_psk31_busy(void) +int cw_psk_busy(void) { return cw_transmit_ongoing; } diff --git a/src/common/Audio/cw.h b/src/common/Audio/cw.h index 39be9c5..153f219 100644 --- a/src/common/Audio/cw.h +++ b/src/common/Audio/cw.h @@ -1,7 +1,7 @@ /* * The MIT License (MIT) * - * Copyright (c) 2015 Matthias P. Braendli + * Copyright (c) 2019 Matthias P. Braendli * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -22,32 +22,38 @@ * SOFTWARE. */ -#ifndef __CW_H_ -#define __CW_H_ +#pragma once #include #include // Setup the CW generator to create audio samples at the given // samplerate. -void cw_psk31_init(unsigned int samplerate); - -// Append new CW or PSK31 text to transmit -// CW/PSK31 audio centre frequency in Hz -// if dit_duration == 0, message is sent in PSK31 +void cw_psk_init(unsigned int samplerate); + +// Append new CW or PSK text to transmit +// CW/PSK audio centre frequency in Hz +// +// Supported characters for CW: +// All ASCII between '+' and '\', which includes +// numerals and capital letters. +// +// Supported characters for PSK: 7-bit clean ASCII +// +// if dit_duration == -1, message is sent in PSK31 +// if dit_duration == -2, message is sent in PSK63 +// if dit_duration == -3, message is sent in PSK125 // otherwise it is sent in CW, with dit_duration in ms // returns 0 on failure, 1 on success -int cw_psk31_push_message(const char* text, int frequency, int dit_duration); +int cw_psk_push_message(const char* text, int frequency, int dit_duration); -// Write the waveform into the buffer (stereo), both for cw and psk31 -size_t cw_psk31_fill_buffer(int16_t *buf, size_t bufsize); +// Write the waveform into the buffer (stereo), both for cw and psk +size_t cw_psk_fill_buffer(int16_t *buf, size_t bufsize); -// Return 1 if the CW or PSK31 generator is running -int cw_psk31_busy(void); +// Return 1 if the CW or PSK generator is running +int cw_psk_busy(void); void cw_message_sent(const char*); size_t cw_symbol(uint8_t, uint8_t *, size_t); -#endif // __CW_H_ - 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 /* 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 #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 +#include +#include +#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 + +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); diff --git a/src/common/sourcelist.txt b/src/common/sourcelist.txt index b0010be..47526cd 100644 --- a/src/common/sourcelist.txt +++ b/src/common/sourcelist.txt @@ -5,6 +5,7 @@ GPS/gps.c GPS/minmea.c Core/common.c Core/fsm.c +Core/stats.c Core/main.c Audio/cw.c Audio/audio.c -- cgit v1.2.3 From 497a16df708dd8316e658f17127f589e08984547 Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Mon, 1 Jul 2019 23:08:44 +0200 Subject: Fix printf warnings --- src/common/Core/stats.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/common/Core/stats.c b/src/common/Core/stats.c index e2ec937..b38d99d 100644 --- a/src/common/Core/stats.c +++ b/src/common/Core/stats.c @@ -30,10 +30,10 @@ #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 int num_beacons_sent = 0; +static int num_wind_generator_movements = 0; +static int num_tx_switch = 0; +static int num_antibavard = 0; static float battery_min = -1.0f; static float battery_max = -1.0f; static float battery_per_hour[24]; @@ -160,11 +160,11 @@ const char* stats_build_text(void) int time_valid = local_time(&time); uint64_t uptime = timestamp_now(); - uint32_t uptime_j = uptime / (24 * 3600 * 1000); + int uptime_j = uptime / (24 * 3600 * 1000); uptime -= uptime_j * (24 * 3600 * 1000); - uint32_t uptime_h = uptime / (3600 * 1000); + int uptime_h = uptime / (3600 * 1000); uptime -= uptime_h * (24 * 3600 * 1000); - uint32_t uptime_m = uptime / (60 * 1000); + int 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"); -- cgit v1.2.3 From 1a5b10d91cdb36a521c7affa300f30bca679a90f Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Fri, 5 Jul 2019 10:19:49 +0200 Subject: stats: add GNSS SV and QRP percentage. Also use derived time --- src/common/Core/main.c | 15 +++++++++++++-- src/common/Core/stats.c | 42 +++++++++++++++++++++++++++++++++++++----- src/common/Core/stats.h | 4 ++++ 3 files changed, 54 insertions(+), 7 deletions(-) diff --git a/src/common/Core/main.c b/src/common/Core/main.c index b43463a..02fb94c 100644 --- a/src/common/Core/main.c +++ b/src/common/Core/main.c @@ -381,7 +381,6 @@ static void audio_callback(void __attribute__ ((unused))*context, int select_buf } timestamp_last_audio_callback = timestamp_now(); - } static struct tm gps_time; @@ -547,6 +546,8 @@ static void exercise_fsm(void __attribute__ ((unused))*pvParameters) int last_discrim_u = 0; int last_wind_generator_ok = 0; + uint64_t last_qrp_stats_updated = timestamp_now(); + fsm_input.humidity = 0; fsm_input.temp = 15; fsm_input.swr_high = 0; @@ -558,6 +559,14 @@ static void exercise_fsm(void __attribute__ ((unused))*pvParameters) pio_set_fsm_signals(&fsm_input); + const uint64_t now = timestamp_now(); + + // QRP/QRO doesn't change too often, updating every 10s is good enough + if (last_qrp_stats_updated + 10000 < now) { + stats_qrp(fsm_input.qrp); + last_qrp_stats_updated = now; + } + if (last_sq != fsm_input.sq) { last_sq = fsm_input.sq; usart_debug("In SQ %d\r\n", last_sq); @@ -617,7 +626,9 @@ static void exercise_fsm(void __attribute__ ((unused))*pvParameters) struct tm time = {0}; int time_valid = local_time(&time); - // TODO use derived too? + if (!time_valid) { + time_valid = local_derived_time(&time); + } if (time_valid) { fsm_input.send_stats = (time.tm_hour == 22) ? 1 : 0; } diff --git a/src/common/Core/stats.c b/src/common/Core/stats.c index b38d99d..1ef74b6 100644 --- a/src/common/Core/stats.c +++ b/src/common/Core/stats.c @@ -34,6 +34,9 @@ static int num_beacons_sent = 0; static int num_wind_generator_movements = 0; static int num_tx_switch = 0; static int num_antibavard = 0; +static int num_sv_used = 0; +static int num_qro = 0; +static int num_qrp = 0; static float battery_min = -1.0f; static float battery_max = -1.0f; static float battery_per_hour[24]; @@ -71,6 +74,8 @@ static void clear_stats() battery_max = -1.0f; temp_min = -1.0f; temp_max = -1.0f; + num_qro = 0; + num_qrp = 0; for (int i = 0; i < 24; i++) { battery_per_hour[i] = -1.0f; } @@ -118,6 +123,20 @@ void stats_temp(float temp) } } +void stats_qrp(int is_qrp) +{ + if (values_valid == 0) { + clear_stats(); + } + + if (is_qrp) { + num_qrp++; + } + else { + num_qro++; + } +} + void stats_wind_generator_moved() { if (values_valid == 0) { @@ -154,6 +173,15 @@ void stats_anti_bavard_triggered() num_antibavard++; } +void stats_num_gnss_sv(int num_sv) +{ + if (values_valid == 0) { + clear_stats(); + } + + num_sv_used = num_sv; +} + const char* stats_build_text(void) { struct tm time = {0}; @@ -185,11 +213,13 @@ const char* stats_build_text(void) 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" , + "U min,max= %dV%01d,%dV%01d\n" + "Temps QRP= %d%%\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); + battery_max_decivolt / 10, battery_max_decivolt % 10, + 100 * num_qrp / (num_qrp + num_qro)); stats_end_ix += snprintf(stats_text + stats_end_ix, STATS_LEN - 1 - stats_end_ix, "U heures pleines=\n"); @@ -197,7 +227,7 @@ const char* stats_build_text(void) 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]; @@ -217,13 +247,15 @@ const char* stats_build_text(void) "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", + "Nbre anti-bavard= %d\n" + "Sat GPS= %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 + num_antibavard, + num_sv_used ); values_valid = 0; diff --git a/src/common/Core/stats.h b/src/common/Core/stats.h index 06e1515..1897d1e 100644 --- a/src/common/Core/stats.h +++ b/src/common/Core/stats.h @@ -33,5 +33,9 @@ void stats_wind_generator_moved(void); void stats_beacon_sent(void); void stats_tx_switched(void); void stats_anti_bavard_triggered(void); +void stats_num_gnss_sv(int num_sv); + +// Must be called in regular intervals +void stats_qrp(int is_qrp); const char* stats_build_text(void); -- cgit v1.2.3 From 42502fc14501d7726b4f96b58326be035db324c4 Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Fri, 5 Jul 2019 11:16:06 +0200 Subject: Stats: add QSO duration, avoid calling build_text all the time --- src/README.md | 6 ++++++ src/common/Core/fsm.c | 8 +++++++- src/common/Core/main.c | 6 +++--- src/common/Core/stats.c | 44 +++++++++++++++++++++++++++++++++++++++----- src/common/Core/stats.h | 2 +- 5 files changed, 56 insertions(+), 10 deletions(-) diff --git a/src/README.md b/src/README.md index 188bd89..f6733e7 100644 --- a/src/README.md +++ b/src/README.md @@ -41,6 +41,12 @@ Debug avec OpenOCD et GDB load continue +Debug simulateur avec GDB +========================= + + handle SIGUSR1 nostop noignore noprint + handle SIG34 nostop noignore noprint + Analyse statique avec clang =========================== diff --git a/src/common/Core/fsm.c b/src/common/Core/fsm.c index 4daf568..a41ee0e 100644 --- a/src/common/Core/fsm.c +++ b/src/common/Core/fsm.c @@ -525,8 +525,11 @@ void fsm_update() { if (fsm_in.cw_psk_done) { balise_message_clear(); + // The exercise_fsm loop needs to see a 1 to 0 transition on cw_psk_trigger + // so that it considers the STATS2 message. fsm_out.cw_psk_trigger = 0; if (current_state == FSM_BALISE_STATS1) { + fsm_out.msg = NULL; next_state = FSM_BALISE_STATS2; } else { @@ -540,7 +543,10 @@ void fsm_update() { fsm_out.msg_frequency = 588; fsm_out.cw_dit_duration = -3; // PSK125 - fsm_out.msg = stats_build_text(); + // All predecessor states must NULL the fsm_out.msg field! + if (fsm_out.msg == NULL) { + fsm_out.msg = stats_build_text(); + } fsm_out.cw_psk_trigger = 1; if (fsm_in.cw_psk_done) { diff --git a/src/common/Core/main.c b/src/common/Core/main.c index 02fb94c..f4125d1 100644 --- a/src/common/Core/main.c +++ b/src/common/Core/main.c @@ -651,13 +651,14 @@ static void exercise_fsm(void __attribute__ ((unused))*pvParameters) pio_set_tx(fsm_out.tx_on); if (fsm_out.tx_on != last_tx_on) { - stats_tx_switched(); + stats_tx_switched(fsm_out.tx_on); 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_psk_trigger && !cw_last_trigger) { + if (fsm_out.cw_psk_trigger && !cw_last_trigger && fsm_out.msg != NULL) { + fprintf(stderr, "TRIG CW %s\n", fsm_out.msg); 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"); @@ -666,7 +667,6 @@ static void exercise_fsm(void __attribute__ ((unused))*pvParameters) leds_turn_on(LED_ORANGE); } cw_last_trigger = fsm_out.cw_psk_trigger; - } } diff --git a/src/common/Core/stats.c b/src/common/Core/stats.c index 1ef74b6..9a14076 100644 --- a/src/common/Core/stats.c +++ b/src/common/Core/stats.c @@ -43,6 +43,10 @@ static float battery_per_hour[24]; static float temp_min = -1.0f; static float temp_max = -1.0f; +static int last_tx_on_valid = 0; +static uint64_t last_tx_on = 0; +static uint64_t max_qso_duration = 0; + /* Ideas * * Version @@ -76,6 +80,8 @@ static void clear_stats() temp_max = -1.0f; num_qro = 0; num_qrp = 0; + last_tx_on_valid = 0; + max_qso_duration = 0; for (int i = 0; i < 24; i++) { battery_per_hour[i] = -1.0f; } @@ -155,13 +161,26 @@ void stats_beacon_sent() num_beacons_sent++; } -void stats_tx_switched() +void stats_tx_switched(int tx_on) { if (values_valid == 0) { clear_stats(); } num_tx_switch++; + + if (tx_on) { + last_tx_on = timestamp_now(); + fprintf(stderr, "TX on at %lu\n", last_tx_on); + last_tx_on_valid = 1; + } + else if (last_tx_on_valid) { + const uint64_t qso_duration = timestamp_now() - last_tx_on; + fprintf(stderr, "TX off with dur=%lu\n", qso_duration); + if (qso_duration > max_qso_duration) { + max_qso_duration = qso_duration; + } + } } void stats_anti_bavard_triggered() @@ -212,17 +231,23 @@ const char* stats_build_text(void) stats_end_ix += snprintf(stats_text + stats_end_ix, STATS_LEN - 1 - stats_end_ix, "Version= %s\n" - "Uptime= %dj%dh%dm\n" + "Uptime= %dj%dh%dm\n", + vc_get_version(), + uptime_j, uptime_h, uptime_m); + + if (values_valid == 0) { + return stats_text; + } + + stats_end_ix += snprintf(stats_text + stats_end_ix, STATS_LEN - 1 - stats_end_ix, "U min,max= %dV%01d,%dV%01d\n" "Temps QRP= %d%%\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, 100 * num_qrp / (num_qrp + num_qro)); stats_end_ix += snprintf(stats_text + stats_end_ix, STATS_LEN - 1 - stats_end_ix, - "U heures pleines=\n"); + "U heures pleines= "); for (int h = 0; h < 24; h++) { if (battery_per_hour[h] == -1.0f) { @@ -242,12 +267,20 @@ const char* stats_build_text(void) const int temp_min_decidegree = 10.0f * temp_min; const int temp_max_decidegree = 10.0f * temp_max; + uint64_t qso_duration = max_qso_duration; + int qso_duration_h = qso_duration / (3600 * 1000); + qso_duration -= qso_duration_h * ( 3600 * 1000); + int qso_duration_m = qso_duration / (60 * 1000); + qso_duration -= qso_duration_m * (60 * 1000); + int qso_duration_s = qso_duration / (1000); + 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" + "QSO le plus long= %dh%dm%ds\n" "Sat GPS= %d\n", num_wind_generator_movements, temp_min_decidegree / 10, temp_min_decidegree % 10, @@ -255,6 +288,7 @@ const char* stats_build_text(void) num_beacons_sent, num_tx_switch, num_antibavard, + qso_duration_h, qso_duration_m, qso_duration_s, num_sv_used ); diff --git a/src/common/Core/stats.h b/src/common/Core/stats.h index 1897d1e..09f4c17 100644 --- a/src/common/Core/stats.h +++ b/src/common/Core/stats.h @@ -31,7 +31,7 @@ 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_tx_switched(int tx_on); void stats_anti_bavard_triggered(void); void stats_num_gnss_sv(int num_sv); -- cgit v1.2.3