Add SNR and frequency reporting to RX. Code refactoring.

This commit is contained in:
Vasily Evseenko 2024-04-02 02:49:01 +03:00
parent b10a65f77a
commit df43c1a270
16 changed files with 168 additions and 77 deletions

View File

@ -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

View File

@ -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)));
} }

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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