diff --git a/lora_pkt_fwd/src/lora_pkt_fwd.c b/lora_pkt_fwd/src/lora_pkt_fwd.c index 801f28d..7b63b8f 100644 --- a/lora_pkt_fwd/src/lora_pkt_fwd.c +++ b/lora_pkt_fwd/src/lora_pkt_fwd.c @@ -134,6 +134,12 @@ static char serv_port_up[8] = STR(DEFAULT_PORT_UP); /* server port for upstream traffic */ static char serv_port_down[8] = STR(DEFAULT_PORT_DW); /* server port for downstream traffic */ static int keepalive_time = DEFAULT_KEEPALIVE; /* send a PULL_DATA request every X seconds, negative = disabled */ +static bool duty_cycle_enabled = true; +static uint32_t duty_cycle_time_avail = 0; +static uint32_t duty_cycle_period = 3600; // seconds in one hour +static double duty_cycle_ratio = 0.10; // 10% +static uint32_t duty_cycle_time_max = 3600 * 0.10 * 1000u; // max time-on-air in window + /* statistics collection configuration variables */ static unsigned stat_interval = DEFAULT_STAT; /* time interval (in sec) at which statistics are collected and displayed */ @@ -729,8 +746,32 @@ static int parse_gateway_configuration(const char * conf_file) { push_timeout_half.tv_usec = 500 * (long int)json_value_get_number(val); MSG("INFO: upstream PUSH_DATA time-out is configured to %u ms\n", (unsigned)(push_timeout_half.tv_usec / 500)); } + + /* duty-cycle limiting */ + val = json_object_get_value(conf_obj, "duty_cycle_enabled"); + if (json_value_get_type(val) == JSONBoolean) { + duty_cycle_enabled = (bool)json_value_get_boolean(val); + } + MSG("INFO: duty cycle will%s be enforced\n", (duty_cycle_enabled ? "" : " NOT")); + + if (duty_cycle_enabled) { + val = json_object_get_value(conf_obj, "duty_cycle_period"); + if (val != NULL) { + duty_cycle_period = (unsigned)json_value_get_number(val); + } + MSG("INFO: duty cycle period %u s\n", (duty_cycle_period)); + + val = json_object_get_value(conf_obj, "duty_cycle_ratio"); + if (val != NULL) { + duty_cycle_ratio = (double)json_value_get_number(val); + } + MSG("INFO: duty cycle %f %%\n", (duty_cycle_ratio * 100)); + + duty_cycle_time_max = duty_cycle_period * 1000u * duty_cycle_ratio; + } + /* packet filtering parameters */ val = json_object_get_value(conf_obj, "best_packet_filter"); if (json_value_get_type(val) == JSONBoolean) { fwd_best_pkt = (bool)json_value_get_boolean(val); @@ -1349,6 +1406,8 @@ int main(void) printf("# PUSH_DATA datagrams sent: %u (%u bytes)\n", cp_up_dgram_sent, cp_up_network_byte); printf("# PUSH_DATA acknowledged: %.2f%%\n", 100.0 * up_ack_ratio); printf("### [DOWNSTREAM] ###\n"); + if (duty_cycle_enabled) + printf("# TIME ON AIR available: %u ms\n", duty_cycle_time_avail); printf("# PULL_DATA sent: %u (%.2f%% acknowledged)\n", cp_dw_pull_sent, 100.0 * dw_ack_ratio); printf("# PULL_RESP(onse) datagrams received: %u (%u bytes)\n", cp_dw_dgram_rcv, cp_dw_network_byte); printf("# RF packets sent to concentrator: %u (%u bytes)\n", (cp_nb_tx_ok+cp_nb_tx_fail), cp_dw_payload_byte); @@ -2833,6 +2921,25 @@ void thread_valid(void) { while (!exit_sig && !quit_sig) { wait_ms(1000); + if (duty_cycle_enabled) { + static struct timespec last = { 0, 0 }; + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + + if (last.tv_sec != 0) { + // uint64(now.tv_sec) * 1000 + now.tv_nsec / 1000000 + + duty_cycle_time_avail += difftimespec(now, last) * 1000u * duty_cycle_ratio; + + if (duty_cycle_time_avail > duty_cycle_time_max) { + duty_cycle_time_avail = duty_cycle_time_max; + } + } + + last = now; + } + + /* calculate when the time reference was last updated */ pthread_mutex_lock(&mx_timeref); gps_ref_age = (long)difftime(time(NULL), time_reference_gps.systime);