From 6c214e8e927f389a642a139def09374f8b97611e Mon Sep 17 00:00:00 2001 From: Robert McMahon Date: Sat, 20 Dec 2025 18:35:06 -0800 Subject: [PATCH] udp with trip-times --- components/iperf/iperf.c | 104 ++++++++++++++++++++++----------------- 1 file changed, 59 insertions(+), 45 deletions(-) diff --git a/components/iperf/iperf.c b/components/iperf/iperf.c index 16f05fe..3e2b53b 100644 --- a/components/iperf/iperf.c +++ b/components/iperf/iperf.c @@ -30,20 +30,19 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ - -/** - * @file iperf.c - * @brief ESP32 iPerf Traffic Generator (UDP Client Only) - * - * This module implements a lightweight UDP traffic generator compatible with iPerf2. - * It features: - * - Precise packet pacing (PPS) using monotonic timers (drift-free). - * - Finite State Machine (FSM) to detect stalls and slow links. - * - Non-Volatile Storage (NVS) for persistent configuration. - * - Detailed error tracking (ENOMEM vs Route errors). - * - GPS Timestamp integration for status reporting. - */ - + /** + * @file iperf.c + * @brief ESP32 iPerf Traffic Generator (UDP Client Only) + * + * This module implements a lightweight UDP traffic generator compatible with iPerf2. + * It features: + * - Precise packet pacing (PPS) using monotonic timers (drift-free). + * - Finite State Machine (FSM) to detect stalls and slow links. + * - Non-Volatile Storage (NVS) for persistent configuration. + * - Detailed error tracking (ENOMEM vs Route errors). + * - GPS Timestamp integration for status reporting. + * @brief ESP32 iPerf Traffic Generator (UDP Client Only) with Trip-Time Support + */ #include #include #include @@ -130,23 +129,22 @@ static uint32_t s_edge_stalled = 0; static esp_event_handler_instance_t instance_any_id; static esp_event_handler_instance_t instance_got_ip; -// --- Packet Structures --- -typedef struct { +// --- Packet Structures & Constants (Compatible with payloads.h) --- + +#define HEADER_EXTEND 0x80000000 +#define HEADER_SEQNO64B 0x08000000 +#define HEADER_TRIPTIME 0x00020000 // Use the small trip time flag + +// UDP Datagram Header +struct udp_datagram { int32_t id; uint32_t tv_sec; uint32_t tv_usec; int32_t id2; -} udp_datagram; - -typedef struct { - int32_t flags; - int32_t numThreads; - int32_t mPort; - int32_t mBufLen; - int32_t mWinBand; - int32_t mAmount; -} client_hdr_v1; - + uint16_t flags; + uint32_t start_tv_sec; + uint32_t start_tv_usec; +}; // --- Helper: Defaults --- static void set_defaults(iperf_cfg_t *cfg) { @@ -326,12 +324,10 @@ void iperf_print_status(void) { (double)s_time_slow_us/1000000.0, pct_slow, (unsigned long)s_edge_slow, (double)s_time_stalled_us/1000000.0, pct_stalled, (unsigned long)s_edge_stalled); - if (s_stats.err_mem > 0 || s_stats.err_route > 0 || s_stats.err_other > 0) { - printf("ERRORS: ENOMEM=%lu, EHOST=%lu, OTHER=%lu\n", - (unsigned long)s_stats.err_mem, - (unsigned long)s_stats.err_route, - (unsigned long)s_stats.err_other); - } + printf("ERRORS: ENOMEM=%lu, EHOST=%lu, OTHER=%lu\n", + (unsigned long)s_stats.err_mem, + (unsigned long)s_stats.err_route, + (unsigned long)s_stats.err_other); } // --- Core Logic --- @@ -340,9 +336,17 @@ static void iperf_pattern(uint8_t *buf, uint32_t len) { for (uint32_t i = 0; i < len; i++) buf[i] = (i % 10) + '0'; } -static void iperf_generate_client_hdr(iperf_cfg_t *cfg, client_hdr_v1 *hdr) { - memset(hdr, 0, sizeof(client_hdr_v1)); - hdr->flags = htonl(0x08000000); // HEADER_SEQNO64B +static void iperf_generate_headers(iperf_cfg_t *cfg, uint8_t *buffer, bool gps_synced, struct timespec *start_time) { + struct udp_datagram *udp_hdr = (struct udp_datagram *)buffer; + // Clear Header Area + memset(udp_hdr, 0, sizeof(struct udp_datagram)); + if (gps_synced) { + udp_hdr->flags = htonl(HEADER_EXTEND | HEADER_SEQNO64B | HEADER_TRIPTIME); + udp_hdr->start_tv_sec = htonl(start_time->tv_sec); + udp_hdr->start_tv_usec = htonl(start_time->tv_nsec / 1000); + } else { + udp_hdr->flags = htonl(HEADER_EXTEND | HEADER_SEQNO64B); + } } static void iperf_network_event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) { @@ -352,8 +356,6 @@ static void iperf_network_event_handler(void* arg, esp_event_base_t event_base, } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { xEventGroupClearBits(s_iperf_event_group, IPERF_IP_READY_BIT); - - // --- ADDED: Set Yellow on Disconnect --- status_led_set_state(LED_STATE_NO_CONFIG); } } @@ -370,7 +372,6 @@ static bool iperf_wait_for_ip(void) { ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &iperf_network_event_handler, NULL, &instance_any_id)); ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &iperf_network_event_handler, NULL, &instance_got_ip)); - // --- ADDED: Set Blue Blink while waiting --- status_led_set_state(LED_STATE_WAITING); ESP_LOGI(TAG, "Waiting for IP..."); @@ -384,7 +385,6 @@ static bool iperf_wait_for_ip(void) { } static esp_err_t iperf_start_udp_client(iperf_ctrl_t *ctrl) { - // Note: status_led_set_state(LED_STATE_WAITING) happens inside if needed if (!iperf_wait_for_ip()) return ESP_OK; struct sockaddr_in addr; @@ -395,16 +395,28 @@ static esp_err_t iperf_start_udp_client(iperf_ctrl_t *ctrl) { int sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (sockfd < 0) { ESP_LOGE(TAG, "Socket failed: %d", errno); - status_led_set_state(LED_STATE_FAILED); // Red Blink on Error + status_led_set_state(LED_STATE_FAILED); return ESP_FAIL; } - // --- Set Purple Slow Pulse (Starting) --- status_led_set_state(LED_STATE_TRANSMITTING_SLOW); - udp_datagram *udp_hdr = (udp_datagram *)ctrl->buffer; - client_hdr_v1 *client_hdr = (client_hdr_v1 *)(ctrl->buffer + sizeof(udp_datagram)); - iperf_generate_client_hdr(&ctrl->cfg, client_hdr); + // --- CHECK GPS SYNC --- + gps_timestamp_t gps = gps_get_timestamp(); + bool gps_synced = gps.synced; + struct timespec start_ts = {0}; + + if (gps_synced) { + ESP_LOGI(TAG, "GPS Synced. Enabling Trip-Times (OWD)."); + // Capture START TIME for session + clock_gettime(CLOCK_REALTIME, &start_ts); + } else { + ESP_LOGW(TAG, "GPS NOT Synced. Trip-Times disabled."); + } + + // --- GENERATE HEADERS --- + // We pass the start_ts so it can be written into the start_fq header + iperf_generate_headers(&ctrl->cfg, ctrl->buffer, gps_synced, &start_ts); s_stats.running = true; s_stats.err_mem = 0; s_stats.err_route = 0; s_stats.err_other = 0; @@ -439,6 +451,7 @@ static esp_err_t iperf_start_udp_client(iperf_ctrl_t *ctrl) { for (int k = 0; k < ctrl->cfg.burst_count; k++) { int64_t current_id = packet_id++; + struct udp_datagram *udp_hdr = (struct udp_datagram *)ctrl->buffer; udp_hdr->id = htonl((uint32_t)(current_id & 0xFFFFFFFF)); udp_hdr->id2 = htonl((uint32_t)((current_id >> 32) & 0xFFFFFFFF)); @@ -503,6 +516,7 @@ static esp_err_t iperf_start_udp_client(iperf_ctrl_t *ctrl) { } int64_t final_id = -packet_id; + struct udp_datagram *udp_hdr = (struct udp_datagram *)ctrl->buffer; udp_hdr->id = htonl((uint32_t)(final_id & 0xFFFFFFFF)); udp_hdr->id2 = htonl((uint32_t)((final_id >> 32) & 0xFFFFFFFF)); clock_gettime(CLOCK_REALTIME, &ts);