mirror of
https://github.com/svpcom/wfb-ng.git
synced 2025-02-22 08:23:47 -04:00
Add SNR and frequency reporting to RX. Code refactoring.
This commit is contained in:
parent
b10a65f77a
commit
df43c1a270
@ -1,6 +1,6 @@
|
|||||||
/* -*- c -*-
|
/* -*- c -*-
|
||||||
*/
|
*/
|
||||||
// Copyright (C) 2017 - 2022 Vasily Evseenko <svpcom@p2ptech.org>
|
// Copyright (C) 2017 - 2024 Vasily Evseenko <svpcom@p2ptech.org>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
50
src/rx.cpp
50
src/rx.cpp
@ -1,6 +1,6 @@
|
|||||||
// -*- C++ -*-
|
// -*- C++ -*-
|
||||||
//
|
//
|
||||||
// Copyright (C) 2017 - 2022 Vasily Evseenko <svpcom@p2ptech.org>
|
// Copyright (C) 2017 - 2024 Vasily Evseenko <svpcom@p2ptech.org>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
@ -111,8 +111,10 @@ void Receiver::loop_iter(void)
|
|||||||
int pktlen = hdr.caplen;
|
int pktlen = hdr.caplen;
|
||||||
// int pkt_rate = 0
|
// int pkt_rate = 0
|
||||||
int ant_idx = 0;
|
int ant_idx = 0;
|
||||||
|
uint32_t freq = 0;
|
||||||
uint8_t antenna[RX_ANT_MAX];
|
uint8_t antenna[RX_ANT_MAX];
|
||||||
int8_t rssi[RX_ANT_MAX];
|
int8_t rssi[RX_ANT_MAX];
|
||||||
|
int8_t noise[RX_ANT_MAX];
|
||||||
uint8_t flags = 0;
|
uint8_t flags = 0;
|
||||||
bool self_injected = false;
|
bool self_injected = false;
|
||||||
struct ieee80211_radiotap_iterator iterator;
|
struct ieee80211_radiotap_iterator iterator;
|
||||||
@ -122,6 +124,8 @@ void Receiver::loop_iter(void)
|
|||||||
memset(antenna, 0xff, sizeof(antenna));
|
memset(antenna, 0xff, sizeof(antenna));
|
||||||
// Fill all rssi slots with minimum value
|
// Fill all rssi slots with minimum value
|
||||||
memset(rssi, SCHAR_MIN, sizeof(rssi));
|
memset(rssi, SCHAR_MIN, sizeof(rssi));
|
||||||
|
// Fill all noise slots with maximum value
|
||||||
|
memset(noise, SCHAR_MAX, sizeof(noise));
|
||||||
|
|
||||||
while (ret == 0 && ant_idx < RX_ANT_MAX) {
|
while (ret == 0 && ant_idx < RX_ANT_MAX) {
|
||||||
ret = ieee80211_radiotap_iterator_next(&iterator);
|
ret = ieee80211_radiotap_iterator_next(&iterator);
|
||||||
@ -145,11 +149,19 @@ void Receiver::loop_iter(void)
|
|||||||
ant_idx += 1;
|
ant_idx += 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case IEEE80211_RADIOTAP_CHANNEL:
|
||||||
|
// drop channel flags - they are redundant for freq to chan convertion
|
||||||
|
freq = le32toh(*(uint32_t*)(iterator.this_arg)) & 0xffff;
|
||||||
|
break;
|
||||||
|
|
||||||
case IEEE80211_RADIOTAP_DBM_ANTSIGNAL:
|
case IEEE80211_RADIOTAP_DBM_ANTSIGNAL:
|
||||||
// Some cards can provide rssi for multiple antennas in one packet, so we should select maximum value
|
|
||||||
rssi[ant_idx] = *(int8_t*)(iterator.this_arg);
|
rssi[ant_idx] = *(int8_t*)(iterator.this_arg);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case IEEE80211_RADIOTAP_DBM_ANTNOISE:
|
||||||
|
noise[ant_idx] = *(int8_t*)(iterator.this_arg);
|
||||||
|
break;
|
||||||
|
|
||||||
case IEEE80211_RADIOTAP_FLAGS:
|
case IEEE80211_RADIOTAP_FLAGS:
|
||||||
flags = *(uint8_t*)(iterator.this_arg);
|
flags = *(uint8_t*)(iterator.this_arg);
|
||||||
break;
|
break;
|
||||||
@ -191,7 +203,7 @@ void Receiver::loop_iter(void)
|
|||||||
|
|
||||||
if (pktlen > (int)sizeof(ieee80211_header))
|
if (pktlen > (int)sizeof(ieee80211_header))
|
||||||
{
|
{
|
||||||
agg->process_packet(pkt + sizeof(ieee80211_header), pktlen - sizeof(ieee80211_header), wlan_idx, antenna, rssi, NULL);
|
agg->process_packet(pkt + sizeof(ieee80211_header), pktlen - sizeof(ieee80211_header), wlan_idx, antenna, rssi, noise, freq, NULL);
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "Short packet (ieee header)\n");
|
fprintf(stderr, "Short packet (ieee header)\n");
|
||||||
continue;
|
continue;
|
||||||
@ -295,12 +307,14 @@ Forwarder::Forwarder(const string &client_addr, int client_port)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Forwarder::process_packet(const uint8_t *buf, size_t size, uint8_t wlan_idx, const uint8_t *antenna, const int8_t *rssi, sockaddr_in *sockaddr)
|
void Forwarder::process_packet(const uint8_t *buf, size_t size, uint8_t wlan_idx, const uint8_t *antenna, const int8_t *rssi, const int8_t *noise, uint16_t freq, sockaddr_in *sockaddr)
|
||||||
{
|
{
|
||||||
wrxfwd_t fwd_hdr = { .wlan_idx = wlan_idx };
|
wrxfwd_t fwd_hdr = { .wlan_idx = wlan_idx,
|
||||||
|
.freq = freq };
|
||||||
|
|
||||||
memcpy(fwd_hdr.antenna, antenna, RX_ANT_MAX * sizeof(uint8_t));
|
memcpy(fwd_hdr.antenna, antenna, RX_ANT_MAX * sizeof(uint8_t));
|
||||||
memcpy(fwd_hdr.rssi, rssi, RX_ANT_MAX * sizeof(int8_t));
|
memcpy(fwd_hdr.rssi, rssi, RX_ANT_MAX * sizeof(int8_t));
|
||||||
|
memcpy(fwd_hdr.noise, noise, RX_ANT_MAX * sizeof(int8_t));
|
||||||
|
|
||||||
struct iovec iov[2] = {{ .iov_base = (void*)&fwd_hdr,
|
struct iovec iov[2] = {{ .iov_base = (void*)&fwd_hdr,
|
||||||
.iov_len = sizeof(fwd_hdr)},
|
.iov_len = sizeof(fwd_hdr)},
|
||||||
@ -400,7 +414,10 @@ void Aggregator::dump_stats(FILE *fp)
|
|||||||
|
|
||||||
for(rx_antenna_stat_t::iterator it = antenna_stat.begin(); it != antenna_stat.end(); it++)
|
for(rx_antenna_stat_t::iterator it = antenna_stat.begin(); it != antenna_stat.end(); it++)
|
||||||
{
|
{
|
||||||
fprintf(fp, "%" PRIu64 "\tRX_ANT\t%" PRIx64 "\t%d:%d:%d:%d\n", ts, it->first, it->second.count_all, it->second.rssi_min, it->second.rssi_sum / it->second.count_all, it->second.rssi_max);
|
fprintf(fp, "%" PRIu64 "\tRX_ANT\t%u\t%" PRIx64 "\t%d" ":%d:%d:%d" ":%d:%d:%d\n",
|
||||||
|
ts, it->first.freq, it->first.antenna_id, it->second.count_all,
|
||||||
|
it->second.rssi_min, it->second.rssi_sum / it->second.count_all, it->second.rssi_max,
|
||||||
|
it->second.snr_min, it->second.snr_sum / it->second.count_all, it->second.snr_max);
|
||||||
}
|
}
|
||||||
antenna_stat.clear();
|
antenna_stat.clear();
|
||||||
|
|
||||||
@ -428,25 +445,26 @@ void Aggregator::dump_stats(FILE *fp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Aggregator::log_rssi(const sockaddr_in *sockaddr, uint8_t wlan_idx, const uint8_t *ant, const int8_t *rssi)
|
void Aggregator::log_rssi(const sockaddr_in *sockaddr, uint8_t wlan_idx, const uint8_t *ant, const int8_t *rssi, const int8_t *noise, uint16_t freq)
|
||||||
{
|
{
|
||||||
for(int i = 0; i < RX_ANT_MAX && ant[i] != 0xff; i++)
|
for(int i = 0; i < RX_ANT_MAX && ant[i] != 0xff; i++)
|
||||||
{
|
{
|
||||||
// key: addr + port + wlan_idx + ant
|
// antenna_id: addr + port + wlan_idx + ant
|
||||||
uint64_t key = 0;
|
rxAntennaKey key = {.freq = freq, .antenna_id = 0};
|
||||||
|
|
||||||
if (sockaddr != NULL && sockaddr->sin_family == AF_INET)
|
if (sockaddr != NULL && sockaddr->sin_family == AF_INET)
|
||||||
{
|
{
|
||||||
key = ((uint64_t)ntohl(sockaddr->sin_addr.s_addr) << 32 | (uint64_t)ntohs(sockaddr->sin_port) << 16);
|
key.antenna_id = ((uint64_t)ntohl(sockaddr->sin_addr.s_addr) << 32 | (uint64_t)ntohs(sockaddr->sin_port) << 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
key |= ((uint64_t)wlan_idx << 8 | (uint64_t)ant[i]);
|
key.antenna_id |= ((uint64_t)wlan_idx << 8 | (uint64_t)ant[i]);
|
||||||
|
|
||||||
antenna_stat[key].log_rssi(rssi[i]);
|
antenna_stat[key].log_rssi(rssi[i], noise[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Aggregator::process_packet(const uint8_t *buf, size_t size, uint8_t wlan_idx, const uint8_t *antenna, const int8_t *rssi, sockaddr_in *sockaddr)
|
void Aggregator::process_packet(const uint8_t *buf, size_t size, uint8_t wlan_idx, const uint8_t *antenna, const int8_t *rssi, const int8_t *noise, uint16_t freq, sockaddr_in *sockaddr)
|
||||||
{
|
{
|
||||||
wsession_data_t new_session_data;
|
wsession_data_t new_session_data;
|
||||||
count_p_all += 1;
|
count_p_all += 1;
|
||||||
@ -526,7 +544,7 @@ void Aggregator::process_packet(const uint8_t *buf, size_t size, uint8_t wlan_id
|
|||||||
}
|
}
|
||||||
|
|
||||||
count_p_dec_ok += 1;
|
count_p_dec_ok += 1;
|
||||||
log_rssi(sockaddr, wlan_idx, antenna, rssi);
|
log_rssi(sockaddr, wlan_idx, antenna, rssi, noise, freq);
|
||||||
|
|
||||||
if (memcmp(session_key, new_session_data.session_key, sizeof(session_key)) != 0)
|
if (memcmp(session_key, new_session_data.session_key, sizeof(session_key)) != 0)
|
||||||
{
|
{
|
||||||
@ -569,7 +587,7 @@ void Aggregator::process_packet(const uint8_t *buf, size_t size, uint8_t wlan_id
|
|||||||
}
|
}
|
||||||
|
|
||||||
count_p_dec_ok += 1;
|
count_p_dec_ok += 1;
|
||||||
log_rssi(sockaddr, wlan_idx, antenna, rssi);
|
log_rssi(sockaddr, wlan_idx, antenna, rssi, noise, freq);
|
||||||
|
|
||||||
assert(decrypted_len <= MAX_FEC_PAYLOAD);
|
assert(decrypted_len <= MAX_FEC_PAYLOAD);
|
||||||
|
|
||||||
@ -872,7 +890,7 @@ void network_loop(int srv_port, Aggregator &agg, int log_interval, int rcv_buf_s
|
|||||||
fprintf(stderr, "Short packet (rx fwd header)\n");
|
fprintf(stderr, "Short packet (rx fwd header)\n");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
agg.process_packet(buf, rsize - sizeof(wrxfwd_t), fwd_hdr.wlan_idx, fwd_hdr.antenna, fwd_hdr.rssi, &sockaddr);
|
agg.process_packet(buf, rsize - sizeof(wrxfwd_t), fwd_hdr.wlan_idx, fwd_hdr.antenna, fwd_hdr.rssi, fwd_hdr.noise, fwd_hdr.freq, &sockaddr);
|
||||||
}
|
}
|
||||||
if(errno != EWOULDBLOCK) throw runtime_error(string_format("Error receiving packet: %s", strerror(errno)));
|
if(errno != EWOULDBLOCK) throw runtime_error(string_format("Error receiving packet: %s", strerror(errno)));
|
||||||
}
|
}
|
||||||
|
50
src/rx.hpp
50
src/rx.hpp
@ -1,6 +1,6 @@
|
|||||||
// -*- C++ -*-
|
// -*- C++ -*-
|
||||||
//
|
//
|
||||||
// Copyright (C) 2017 - 2022 Vasily Evseenko <svpcom@p2ptech.org>
|
// Copyright (C) 2017 - 2024 Vasily Evseenko <svpcom@p2ptech.org>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
@ -39,7 +39,7 @@ typedef enum {
|
|||||||
class BaseAggregator
|
class BaseAggregator
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual void process_packet(const uint8_t *buf, size_t size, uint8_t wlan_idx, const uint8_t *antenna, const int8_t *rssi, sockaddr_in *sockaddr) = 0;
|
virtual void process_packet(const uint8_t *buf, size_t size, uint8_t wlan_idx, const uint8_t *antenna, const int8_t *rssi, const int8_t *noise, uint16_t freq, sockaddr_in *sockaddr) = 0;
|
||||||
virtual void dump_stats(FILE *fp) = 0;
|
virtual void dump_stats(FILE *fp) = 0;
|
||||||
protected:
|
protected:
|
||||||
int open_udp_socket_for_tx(const std::string &client_addr, int client_port)
|
int open_udp_socket_for_tx(const std::string &client_addr, int client_port)
|
||||||
@ -67,7 +67,7 @@ class Forwarder : public BaseAggregator
|
|||||||
public:
|
public:
|
||||||
Forwarder(const std::string &client_addr, int client_port);
|
Forwarder(const std::string &client_addr, int client_port);
|
||||||
~Forwarder();
|
~Forwarder();
|
||||||
virtual void process_packet(const uint8_t *buf, size_t size, uint8_t wlan_idx, const uint8_t *antenna, const int8_t *rssi, sockaddr_in *sockaddr);
|
virtual void process_packet(const uint8_t *buf, size_t size, uint8_t wlan_idx, const uint8_t *antenna, const int8_t *rssi, const int8_t *noise, uint16_t freq, sockaddr_in *sockaddr);
|
||||||
virtual void dump_stats(FILE *) {}
|
virtual void dump_stats(FILE *) {}
|
||||||
private:
|
private:
|
||||||
int sockfd;
|
int sockfd;
|
||||||
@ -93,17 +93,26 @@ static inline int modN(int x, int base)
|
|||||||
class rxAntennaItem
|
class rxAntennaItem
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
rxAntennaItem(void) : count_all(0), rssi_sum(0), rssi_min(0), rssi_max(0) {}
|
rxAntennaItem(void) : count_all(0),
|
||||||
|
rssi_sum(0), rssi_min(0), rssi_max(0),
|
||||||
|
snr_sum(0), snr_min(0), snr_max(0) {}
|
||||||
|
|
||||||
|
void log_rssi(int8_t rssi, int8_t noise){
|
||||||
|
int8_t snr = (noise != SCHAR_MAX) ? rssi - noise : 0;
|
||||||
|
|
||||||
void log_rssi(int8_t rssi){
|
|
||||||
if(count_all == 0){
|
if(count_all == 0){
|
||||||
rssi_min = rssi;
|
rssi_min = rssi;
|
||||||
rssi_max = rssi;
|
rssi_max = rssi;
|
||||||
|
snr_min = snr;
|
||||||
|
snr_max = snr;
|
||||||
} else {
|
} else {
|
||||||
rssi_min = std::min(rssi, rssi_min);
|
rssi_min = std::min(rssi, rssi_min);
|
||||||
rssi_max = std::max(rssi, rssi_max);
|
rssi_max = std::max(rssi, rssi_max);
|
||||||
|
snr_min = std::min(snr, snr_min);
|
||||||
|
snr_max = std::max(snr, snr_max);
|
||||||
}
|
}
|
||||||
rssi_sum += rssi;
|
rssi_sum += rssi;
|
||||||
|
snr_sum += snr;
|
||||||
count_all += 1;
|
count_all += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,23 +120,48 @@ public:
|
|||||||
int32_t rssi_sum;
|
int32_t rssi_sum;
|
||||||
int8_t rssi_min;
|
int8_t rssi_min;
|
||||||
int8_t rssi_max;
|
int8_t rssi_max;
|
||||||
|
int32_t snr_sum;
|
||||||
|
int8_t snr_min;
|
||||||
|
int8_t snr_max;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::unordered_map<uint64_t, rxAntennaItem> rx_antenna_stat_t;
|
struct rxAntennaKey
|
||||||
|
{
|
||||||
|
uint16_t freq;
|
||||||
|
uint64_t antenna_id;
|
||||||
|
|
||||||
|
bool operator==(const rxAntennaKey &other) const
|
||||||
|
{
|
||||||
|
return (freq == other.freq && antenna_id == other.antenna_id);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct std::hash<rxAntennaKey>
|
||||||
|
{
|
||||||
|
std::size_t operator()(const rxAntennaKey& k) const noexcept
|
||||||
|
{
|
||||||
|
std::size_t h1 = std::hash<uint16_t>{}(k.freq);
|
||||||
|
std::size_t h2 = std::hash<uint64_t>{}(k.antenna_id);
|
||||||
|
return h1 ^ (h2 << 1); // combine hashes
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef std::unordered_map<rxAntennaKey, rxAntennaItem> rx_antenna_stat_t;
|
||||||
|
|
||||||
class Aggregator : public BaseAggregator
|
class Aggregator : public BaseAggregator
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Aggregator(const std::string &client_addr, int client_port, const std::string &keypair, uint64_t epoch, uint32_t channel_id);
|
Aggregator(const std::string &client_addr, int client_port, const std::string &keypair, uint64_t epoch, uint32_t channel_id);
|
||||||
~Aggregator();
|
~Aggregator();
|
||||||
virtual void process_packet(const uint8_t *buf, size_t size, uint8_t wlan_idx, const uint8_t *antenna, const int8_t *rssi, sockaddr_in *sockaddr);
|
virtual void process_packet(const uint8_t *buf, size_t size, uint8_t wlan_idx, const uint8_t *antenna, const int8_t *rssi, const int8_t *noise, uint16_t freq, sockaddr_in *sockaddr);
|
||||||
virtual void dump_stats(FILE *fp);
|
virtual void dump_stats(FILE *fp);
|
||||||
private:
|
private:
|
||||||
void init_fec(int k, int n);
|
void init_fec(int k, int n);
|
||||||
void deinit_fec(void);
|
void deinit_fec(void);
|
||||||
void send_packet(int ring_idx, int fragment_idx);
|
void send_packet(int ring_idx, int fragment_idx);
|
||||||
void apply_fec(int ring_idx);
|
void apply_fec(int ring_idx);
|
||||||
void log_rssi(const sockaddr_in *sockaddr, uint8_t wlan_idx, const uint8_t *ant, const int8_t *rssi);
|
void log_rssi(const sockaddr_in *sockaddr, uint8_t wlan_idx, const uint8_t *ant, const int8_t *rssi, const int8_t *noise, uint16_t freq);
|
||||||
int get_block_ring_idx(uint64_t block_idx);
|
int get_block_ring_idx(uint64_t block_idx);
|
||||||
int rx_ring_push(void);
|
int rx_ring_push(void);
|
||||||
fec_t* fec_p;
|
fec_t* fec_p;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// -*- C++ -*-
|
// -*- C++ -*-
|
||||||
//
|
//
|
||||||
// Copyright (C) 2017 - 2022 Vasily Evseenko <svpcom@p2ptech.org>
|
// Copyright (C) 2017 - 2024 Vasily Evseenko <svpcom@p2ptech.org>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// -*- C++ -*-
|
// -*- C++ -*-
|
||||||
//
|
//
|
||||||
// Copyright (C) 2017 - 2022 Vasily Evseenko <svpcom@p2ptech.org>
|
// Copyright (C) 2017 - 2024 Vasily Evseenko <svpcom@p2ptech.org>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright (C) 2017 - 2022 Vasily Evseenko <svpcom@p2ptech.org>
|
// Copyright (C) 2017 - 2024 Vasily Evseenko <svpcom@p2ptech.org>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright (C) 2017 - 2022 Vasily Evseenko <svpcom@p2ptech.org>
|
// Copyright (C) 2017 - 2024 Vasily Evseenko <svpcom@p2ptech.org>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
@ -149,6 +149,8 @@ typedef struct {
|
|||||||
uint8_t wlan_idx;
|
uint8_t wlan_idx;
|
||||||
uint8_t antenna[RX_ANT_MAX]; //RADIOTAP_ANTENNA, list of antenna idx, 0xff for unused slot
|
uint8_t antenna[RX_ANT_MAX]; //RADIOTAP_ANTENNA, list of antenna idx, 0xff for unused slot
|
||||||
int8_t rssi[RX_ANT_MAX]; //RADIOTAP_DBM_ANTSIGNAL, list of rssi for corresponding antenna idx
|
int8_t rssi[RX_ANT_MAX]; //RADIOTAP_DBM_ANTSIGNAL, list of rssi for corresponding antenna idx
|
||||||
|
int8_t noise[RX_ANT_MAX]; //RADIOTAP_DBM_ANTNOISE, list of (rssi - snr) for corresponding antenna idx
|
||||||
|
uint16_t freq; //IEEE80211_RADIOTAP_CHANNEL -- channel frequency in MHz
|
||||||
} __attribute__ ((packed)) wrxfwd_t;
|
} __attribute__ ((packed)) wrxfwd_t;
|
||||||
|
|
||||||
// Network packet headers. All numbers are in network (big endian) format
|
// Network packet headers. All numbers are in network (big endian) format
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
[DEFAULT]
|
[DEFAULT]
|
||||||
Depends3: python3-twisted, libpcap-dev, libsodium-dev, python3-pyroute2, python3-future, python3-serial
|
Depends3: python3-twisted, libpcap-dev, libsodium-dev, python3-pyroute2, python3-future, python3-serial, python3-msgpack
|
||||||
Package3: wfb-ng
|
Package3: wfb-ng
|
||||||
Replaces3: wifibroadcast
|
Replaces3: wifibroadcast
|
||||||
Maintainer: Vasily Evseenko <svpcom@p2ptech.org>
|
Maintainer: Vasily Evseenko <svpcom@p2ptech.org>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright (C) 2018-2022 Vasily Evseenko <svpcom@p2ptech.org>
|
# Copyright (C) 2018-2024 Vasily Evseenko <svpcom@p2ptech.org>
|
||||||
|
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or modify
|
# This program is free software; you can redistribute it and/or modify
|
||||||
@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
import sys
|
import sys
|
||||||
import curses
|
import curses
|
||||||
import json
|
import msgpack
|
||||||
import tempfile
|
import tempfile
|
||||||
import signal
|
import signal
|
||||||
import termios
|
import termios
|
||||||
@ -28,7 +28,7 @@ import termios
|
|||||||
from twisted.python import log
|
from twisted.python import log
|
||||||
from twisted.internet import reactor, defer
|
from twisted.internet import reactor, defer
|
||||||
from twisted.internet.protocol import ReconnectingClientFactory
|
from twisted.internet.protocol import ReconnectingClientFactory
|
||||||
from twisted.protocols.basic import LineReceiver
|
from twisted.protocols.basic import Int32StringReceiver
|
||||||
from .server import parse_services
|
from .server import parse_services
|
||||||
from .common import abort_on_crash, exit_status
|
from .common import abort_on_crash, exit_status
|
||||||
from .conf import settings
|
from .conf import settings
|
||||||
@ -65,11 +65,11 @@ def addstr(window, y, x, s, *attrs):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class AntennaStat(LineReceiver):
|
class AntennaStat(Int32StringReceiver):
|
||||||
delimiter = b'\n'
|
MAX_LENGTH = 1024 * 1024
|
||||||
|
|
||||||
def lineReceived(self, line):
|
def stringReceived(self, string):
|
||||||
attrs = json.loads(line)
|
attrs = msgpack.unpackb(string, strict_map_key=False, use_list=False)
|
||||||
|
|
||||||
if attrs['type'] == 'rx':
|
if attrs['type'] == 'rx':
|
||||||
self.draw_rx(attrs)
|
self.draw_rx(attrs)
|
||||||
@ -78,7 +78,7 @@ class AntennaStat(LineReceiver):
|
|||||||
|
|
||||||
def draw_rx(self, attrs):
|
def draw_rx(self, attrs):
|
||||||
p = attrs['packets']
|
p = attrs['packets']
|
||||||
rssi_d = attrs['rssi']
|
stats_d = attrs['rx_ant_stats']
|
||||||
tx_ant = attrs.get('tx_ant')
|
tx_ant = attrs.get('tx_ant')
|
||||||
rx_id = attrs['id']
|
rx_id = attrs['id']
|
||||||
|
|
||||||
@ -101,13 +101,16 @@ class AntennaStat(LineReceiver):
|
|||||||
if y < ymax:
|
if y < ymax:
|
||||||
addstr(window, y, 0, msg, attr)
|
addstr(window, y, 0, msg, attr)
|
||||||
|
|
||||||
if rssi_d:
|
if stats_d:
|
||||||
addstr(window, 0, 25, '[ANT] pkt/s RSSI [dBm]')
|
addstr(window, 0, 24, 'Freq [ANT] pkt/s RSSI [dBm] SNR [dB]')
|
||||||
for y, (k, v) in enumerate(sorted(rssi_d.items()), 1):
|
for y, ((freq, ant_id), v) in enumerate(sorted(stats_d.items()), 1):
|
||||||
pkt_s, rssi_min, rssi_avg, rssi_max = v
|
pkt_s, rssi_min, rssi_avg, rssi_max, snr_min, snr_avg, snr_max = v
|
||||||
if y < ymax:
|
if y < ymax:
|
||||||
active_tx = '*' if (int(k, 16) >> 8) == tx_ant else ' '
|
active_tx = '*' if (ant_id >> 8) == tx_ant else ' '
|
||||||
addstr(window, y, 24, '%s%04x: %4d %3d < %3d < %3d' % (active_tx, int(k, 16), pkt_s, rssi_min, rssi_avg, rssi_max))
|
addstr(window, y, 24, '%04d %s%04x %4d %3d < %3d < %3d %3d < %3d < %3d' % \
|
||||||
|
(freq, active_tx, ant_id, pkt_s,
|
||||||
|
rssi_min, rssi_avg, rssi_max,
|
||||||
|
snr_min, snr_avg, snr_max))
|
||||||
else:
|
else:
|
||||||
addstr(window, 0, 25, '[No data]', curses.A_REVERSE)
|
addstr(window, 0, 25, '[No data]', curses.A_REVERSE)
|
||||||
|
|
||||||
@ -139,9 +142,10 @@ class AntennaStat(LineReceiver):
|
|||||||
if latency_d:
|
if latency_d:
|
||||||
addstr(window, 0, 25, '[ANT] pkt/s Injection [us]')
|
addstr(window, 0, 25, '[ANT] pkt/s Injection [us]')
|
||||||
for y, (k, v) in enumerate(sorted(latency_d.items()), 1):
|
for y, (k, v) in enumerate(sorted(latency_d.items()), 1):
|
||||||
|
k = int(k) # json doesn't support int keys
|
||||||
injected, dropped, lat_min, lat_avg, lat_max = v
|
injected, dropped, lat_min, lat_avg, lat_max = v
|
||||||
if y < ymax:
|
if y < ymax:
|
||||||
addstr(window, y, 25, '%04x: %4d %4d < %4d < %4d' % (int(k, 16), injected, lat_min, lat_avg, lat_max))
|
addstr(window, y, 25, '%04x: %4d %4d < %4d < %4d' % (k, injected, lat_min, lat_avg, lat_max))
|
||||||
else:
|
else:
|
||||||
addstr(window, 0, 25, '[No data]', curses.A_REVERSE)
|
addstr(window, 0, 25, '[No data]', curses.A_REVERSE)
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright (C) 2018-2022 Vasily Evseenko <svpcom@p2ptech.org>
|
# Copyright (C) 2018-2024 Vasily Evseenko <svpcom@p2ptech.org>
|
||||||
|
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or modify
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright (C) 2018 - 2022 Vasily Evseenko <svpcom@p2ptech.org>
|
# Copyright (C) 2018-2024 Vasily Evseenko <svpcom@p2ptech.org>
|
||||||
|
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or modify
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright (C) 2018-2022 Vasily Evseenko <svpcom@p2ptech.org>
|
# Copyright (C) 2018-2024 Vasily Evseenko <svpcom@p2ptech.org>
|
||||||
|
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or modify
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright (C) 2018 - 2022 Vasily Evseenko <svpcom@p2ptech.org>
|
# Copyright (C) 2018-2024 Vasily Evseenko <svpcom@p2ptech.org>
|
||||||
|
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or modify
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright (C) 2018-2022 Vasily Evseenko <svpcom@p2ptech.org>
|
# Copyright (C) 2018-2024 Vasily Evseenko <svpcom@p2ptech.org>
|
||||||
|
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or modify
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright (C) 2018-2022 Vasily Evseenko <svpcom@p2ptech.org>
|
# Copyright (C) 2018-2024 Vasily Evseenko <svpcom@p2ptech.org>
|
||||||
|
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or modify
|
# This program is free software; you can redistribute it and/or modify
|
||||||
@ -19,7 +19,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import json
|
import msgpack
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import hashlib
|
import hashlib
|
||||||
@ -29,7 +29,7 @@ from twisted.python import log, failure
|
|||||||
from twisted.python.logfile import LogFile
|
from twisted.python.logfile import LogFile
|
||||||
from twisted.internet import reactor, defer, main as ti_main
|
from twisted.internet import reactor, defer, main as ti_main
|
||||||
from twisted.internet.protocol import ProcessProtocol, Protocol, Factory
|
from twisted.internet.protocol import ProcessProtocol, Protocol, Factory
|
||||||
from twisted.protocols.basic import LineReceiver
|
from twisted.protocols.basic import LineReceiver, Int32StringReceiver
|
||||||
from twisted.internet.serialport import SerialPort
|
from twisted.internet.serialport import SerialPort
|
||||||
|
|
||||||
from . import _log_msg, ConsoleObserver, call_and_check_rc, ExecError
|
from . import _log_msg, ConsoleObserver, call_and_check_rc, ExecError
|
||||||
@ -57,18 +57,20 @@ class WFBFlags(object):
|
|||||||
fec_types = {1: 'VDM_RS'}
|
fec_types = {1: 'VDM_RS'}
|
||||||
|
|
||||||
|
|
||||||
class StatisticsProtocol(Protocol):
|
class StatisticsProtocol(Int32StringReceiver):
|
||||||
|
MAX_LENGTH = 1024 * 1024
|
||||||
|
|
||||||
def connectionMade(self):
|
def connectionMade(self):
|
||||||
self.factory.ui_sessions.append(self)
|
self.factory.ui_sessions.append(self)
|
||||||
|
|
||||||
def dataReceived(self, data):
|
def stringReceived(self, string):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def connectionLost(self, reason):
|
def connectionLost(self, reason):
|
||||||
self.factory.ui_sessions.remove(self)
|
self.factory.ui_sessions.remove(self)
|
||||||
|
|
||||||
def send_stats(self, data):
|
def send_stats(self, data):
|
||||||
self.transport.write(json.dumps(data).encode('utf-8') + b'\n')
|
self.sendString(msgpack.packb(data))
|
||||||
|
|
||||||
|
|
||||||
class StatsAndSelectorFactory(Factory):
|
class StatsAndSelectorFactory(Factory):
|
||||||
@ -97,11 +99,41 @@ class StatsAndSelectorFactory(Factory):
|
|||||||
def add_rssi_cb(self, rssi_cb):
|
def add_rssi_cb(self, rssi_cb):
|
||||||
self.rssi_cb_l.append(rssi_cb)
|
self.rssi_cb_l.append(rssi_cb)
|
||||||
|
|
||||||
def select_tx_antenna(self, ant_rssi):
|
def _stats_agg_by_freq(self, ant_stats):
|
||||||
|
stats_agg = {}
|
||||||
|
|
||||||
|
for (freq, ant_id), (pkt_s,
|
||||||
|
rssi_min, rssi_avg, rssi_max,
|
||||||
|
snr_min, snr_avg, snr_max) in ant_stats.items():
|
||||||
|
|
||||||
|
if ant_id not in stats_agg:
|
||||||
|
stats_agg[ant_id] = (pkt_s,
|
||||||
|
rssi_min, rssi_avg * pkt_s, rssi_max,
|
||||||
|
snr_min, snr_avg * pkt_s, snr_max)
|
||||||
|
else:
|
||||||
|
tmp = stats_agg[ant_id]
|
||||||
|
stats_agg[ant_id] = (pkt_s + tmp[0],
|
||||||
|
min(rssi_min, tmp[1]),
|
||||||
|
rssi_avg * pkt_s + tmp[2],
|
||||||
|
max(rssi_max, tmp[3]),
|
||||||
|
min(snr_min, tmp[4]),
|
||||||
|
snr_avg * pkt_s + tmp[5],
|
||||||
|
max(snr_max, tmp[6]))
|
||||||
|
|
||||||
|
return dict((ant_id, (pkt_s,
|
||||||
|
rssi_min, rssi_avg // pkt_s, rssi_max,
|
||||||
|
snr_min, snr_avg // pkt_s, snr_max)) \
|
||||||
|
for ant_id, (pkt_s,
|
||||||
|
rssi_min, rssi_avg, rssi_max,
|
||||||
|
snr_min, snr_avg, snr_max) in stats_agg.items())
|
||||||
|
|
||||||
|
def select_tx_antenna(self, stats_agg):
|
||||||
wlan_rssi = {}
|
wlan_rssi = {}
|
||||||
|
|
||||||
for k, grp in groupby(sorted(((int(ant_id, 16) >> 8) & 0xff, rssi_avg) \
|
for k, grp in groupby(sorted(((ant_id >> 8) & 0xff, rssi_avg) \
|
||||||
for ant_id, (pkt_s, rssi_min, rssi_avg, rssi_max) in ant_rssi.items()),
|
for ant_id, (pkt_s,
|
||||||
|
rssi_min, rssi_avg, rssi_max,
|
||||||
|
snr_min, snr_avg, snr_max) in stats_agg.items()),
|
||||||
lambda x: x[0]):
|
lambda x: x[0]):
|
||||||
# Select max average rssi [dBm] from all wlan's antennas
|
# Select max average rssi [dBm] from all wlan's antennas
|
||||||
wlan_rssi[k] = max(rssi for _, rssi in grp)
|
wlan_rssi[k] = max(rssi for _, rssi in grp)
|
||||||
@ -125,42 +157,43 @@ class StatsAndSelectorFactory(Factory):
|
|||||||
|
|
||||||
self.tx_sel = max_rssi_ant
|
self.tx_sel = max_rssi_ant
|
||||||
|
|
||||||
def update_rx_stats(self, rx_id, packet_stats, ant_rssi):
|
def update_rx_stats(self, rx_id, packet_stats, ant_stats):
|
||||||
mav_rssi = []
|
stats_agg = self._stats_agg_by_freq(ant_stats)
|
||||||
|
card_rssi_l = list(rssi_avg
|
||||||
|
for pkt_s,
|
||||||
|
rssi_min, rssi_avg, rssi_max,
|
||||||
|
snr_min, snr_avg, snr_max
|
||||||
|
in stats_agg.values())
|
||||||
|
|
||||||
for i, (k, v) in enumerate(sorted(ant_rssi.items())):
|
if stats_agg and self.ant_sel_cb_list:
|
||||||
pkt_s, rssi_min, rssi_avg, rssi_max = v
|
self.select_tx_antenna(stats_agg)
|
||||||
mav_rssi.append(rssi_avg)
|
|
||||||
|
|
||||||
rssi = (max(mav_rssi) if mav_rssi else -128) % 256
|
|
||||||
|
|
||||||
if ant_rssi and self.ant_sel_cb_list:
|
|
||||||
self.select_tx_antenna(ant_rssi)
|
|
||||||
|
|
||||||
if self.rssi_cb_l:
|
if self.rssi_cb_l:
|
||||||
_idx = 0 if settings.common.mavlink_err_rate else 1
|
_idx = 0 if settings.common.mavlink_err_rate else 1
|
||||||
flags = 0
|
flags = 0
|
||||||
|
|
||||||
if not mav_rssi:
|
if not card_rssi_l:
|
||||||
flags |= WFBFlags.LINK_LOST
|
flags |= WFBFlags.LINK_LOST
|
||||||
|
|
||||||
elif packet_stats['dec_err'][0] + packet_stats['bad'][0] > 0:
|
elif packet_stats['dec_err'][0] + packet_stats['bad'][0] > 0:
|
||||||
flags |= WFBFlags.LINK_JAMMED
|
flags |= WFBFlags.LINK_JAMMED
|
||||||
|
|
||||||
rx_errors = min(packet_stats['dec_err'][_idx] + packet_stats['bad'][_idx] + packet_stats['lost'][_idx], 65535)
|
rx_errors = min(packet_stats['dec_err'][_idx] + packet_stats['bad'][_idx] + packet_stats['lost'][_idx], 65535)
|
||||||
rx_fec = min(packet_stats['fec_rec'][_idx], 65535)
|
rx_fec = min(packet_stats['fec_rec'][_idx], 65535)
|
||||||
|
mav_rssi = (max(card_rssi_l) if card_rssi_l else -128) % 256
|
||||||
|
|
||||||
for rssi_cb in self.rssi_cb_l:
|
for rssi_cb in self.rssi_cb_l:
|
||||||
try:
|
try:
|
||||||
rssi_cb(rx_id, rssi, rx_errors, rx_fec, flags)
|
rssi_cb(rx_id, mav_rssi, rx_errors, rx_fec, flags)
|
||||||
except Exception:
|
except Exception:
|
||||||
log.err()
|
log.err()
|
||||||
|
|
||||||
if settings.common.debug:
|
if settings.common.debug:
|
||||||
log.msg('%s rssi %s tx#%d %s %s' % (rx_id, max(mav_rssi) if mav_rssi else 'N/A', self.tx_sel, packet_stats, ant_rssi))
|
log.msg('%s rssi %s tx#%d %s %s' % (rx_id, max(card_rssi_l) if card_rssi_l else 'N/A', self.tx_sel, packet_stats, ant_stats))
|
||||||
|
|
||||||
# Send stats to CLI sessions
|
# Send stats to CLI sessions
|
||||||
for s in self.ui_sessions:
|
for s in self.ui_sessions:
|
||||||
s.send_stats(dict(type='rx', id=rx_id, tx_ant=self.tx_sel, packets=packet_stats, rssi=ant_rssi))
|
s.send_stats(dict(type='rx', id=rx_id, tx_ant=self.tx_sel, packets=packet_stats, rx_ant_stats=ant_stats))
|
||||||
|
|
||||||
def update_tx_stats(self, tx_id, packet_stats, ant_latency):
|
def update_tx_stats(self, tx_id, packet_stats, ant_latency):
|
||||||
if settings.common.debug:
|
if settings.common.debug:
|
||||||
@ -197,9 +230,9 @@ class RXAntennaProtocol(LineReceiver):
|
|||||||
cmd = cols[1]
|
cmd = cols[1]
|
||||||
|
|
||||||
if cmd == 'RX_ANT':
|
if cmd == 'RX_ANT':
|
||||||
if len(cols) != 4:
|
if len(cols) != 5:
|
||||||
raise BadTelemetry()
|
raise BadTelemetry()
|
||||||
self.ant[cols[2]] = tuple(int(i) for i in cols[3].split(':'))
|
self.ant[(int(cols[2]), int(cols[3], 16))] = tuple(int(i) for i in cols[4].split(':'))
|
||||||
|
|
||||||
elif cmd == 'PKT':
|
elif cmd == 'PKT':
|
||||||
if len(cols) != 3:
|
if len(cols) != 3:
|
||||||
@ -280,7 +313,7 @@ class TXAntennaProtocol(LineReceiver):
|
|||||||
elif cmd == 'TX_ANT':
|
elif cmd == 'TX_ANT':
|
||||||
if len(cols) != 4:
|
if len(cols) != 4:
|
||||||
raise BadTelemetry()
|
raise BadTelemetry()
|
||||||
self.ant[cols[2]] = tuple(int(i) for i in cols[3].split(':'))
|
self.ant[int(cols[2], 16)] = tuple(int(i) for i in cols[3].split(':'))
|
||||||
|
|
||||||
elif cmd == 'PKT':
|
elif cmd == 'PKT':
|
||||||
if len(cols) != 3:
|
if len(cols) != 3:
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright (C) 2018-2022 Vasily Evseenko <svpcom@p2ptech.org>
|
# Copyright (C) 2018-2024 Vasily Evseenko <svpcom@p2ptech.org>
|
||||||
|
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or modify
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
Loading…
Reference in New Issue
Block a user