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 -*-
*/
// 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

View File

@ -1,6 +1,6 @@
// -*- 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
@ -111,8 +111,10 @@ void Receiver::loop_iter(void)
int pktlen = hdr.caplen;
// int pkt_rate = 0
int ant_idx = 0;
uint32_t freq = 0;
uint8_t antenna[RX_ANT_MAX];
int8_t rssi[RX_ANT_MAX];
int8_t noise[RX_ANT_MAX];
uint8_t flags = 0;
bool self_injected = false;
struct ieee80211_radiotap_iterator iterator;
@ -122,6 +124,8 @@ void Receiver::loop_iter(void)
memset(antenna, 0xff, sizeof(antenna));
// Fill all rssi slots with minimum value
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) {
ret = ieee80211_radiotap_iterator_next(&iterator);
@ -145,11 +149,19 @@ void Receiver::loop_iter(void)
ant_idx += 1;
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:
// 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);
break;
case IEEE80211_RADIOTAP_DBM_ANTNOISE:
noise[ant_idx] = *(int8_t*)(iterator.this_arg);
break;
case IEEE80211_RADIOTAP_FLAGS:
flags = *(uint8_t*)(iterator.this_arg);
break;
@ -191,7 +203,7 @@ void Receiver::loop_iter(void)
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 {
fprintf(stderr, "Short packet (ieee header)\n");
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.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,
.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++)
{
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();
@ -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++)
{
// key: addr + port + wlan_idx + ant
uint64_t key = 0;
// antenna_id: addr + port + wlan_idx + ant
rxAntennaKey key = {.freq = freq, .antenna_id = 0};
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;
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;
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)
{
@ -569,7 +587,7 @@ void Aggregator::process_packet(const uint8_t *buf, size_t size, uint8_t wlan_id
}
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);
@ -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");
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)));
}

View File

@ -1,6 +1,6 @@
// -*- 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
@ -39,7 +39,7 @@ typedef enum {
class BaseAggregator
{
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;
protected:
int open_udp_socket_for_tx(const std::string &client_addr, int client_port)
@ -67,7 +67,7 @@ class Forwarder : public BaseAggregator
public:
Forwarder(const std::string &client_addr, int client_port);
~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 *) {}
private:
int sockfd;
@ -93,17 +93,26 @@ static inline int modN(int x, int base)
class rxAntennaItem
{
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){
rssi_min = rssi;
rssi_max = rssi;
snr_min = snr;
snr_max = snr;
} else {
rssi_min = std::min(rssi, rssi_min);
rssi_max = std::max(rssi, rssi_max);
snr_min = std::min(snr, snr_min);
snr_max = std::max(snr, snr_max);
}
rssi_sum += rssi;
snr_sum += snr;
count_all += 1;
}
@ -111,23 +120,48 @@ public:
int32_t rssi_sum;
int8_t rssi_min;
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
{
public:
Aggregator(const std::string &client_addr, int client_port, const std::string &keypair, uint64_t epoch, uint32_t channel_id);
~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);
private:
void init_fec(int k, int n);
void deinit_fec(void);
void send_packet(int ring_idx, int fragment_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 rx_ring_push(void);
fec_t* fec_p;

View File

@ -1,6 +1,6 @@
// -*- 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

View File

@ -1,6 +1,6 @@
// -*- 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

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

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
@ -149,6 +149,8 @@ typedef struct {
uint8_t wlan_idx;
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 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;
// Network packet headers. All numbers are in network (big endian) format

View File

@ -1,5 +1,5 @@
[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
Replaces3: wifibroadcast
Maintainer: Vasily Evseenko <svpcom@p2ptech.org>

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python
# -*- 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
@ -20,7 +20,7 @@
import sys
import curses
import json
import msgpack
import tempfile
import signal
import termios
@ -28,7 +28,7 @@ import termios
from twisted.python import log
from twisted.internet import reactor, defer
from twisted.internet.protocol import ReconnectingClientFactory
from twisted.protocols.basic import LineReceiver
from twisted.protocols.basic import Int32StringReceiver
from .server import parse_services
from .common import abort_on_crash, exit_status
from .conf import settings
@ -65,11 +65,11 @@ def addstr(window, y, x, s, *attrs):
pass
class AntennaStat(LineReceiver):
delimiter = b'\n'
class AntennaStat(Int32StringReceiver):
MAX_LENGTH = 1024 * 1024
def lineReceived(self, line):
attrs = json.loads(line)
def stringReceived(self, string):
attrs = msgpack.unpackb(string, strict_map_key=False, use_list=False)
if attrs['type'] == 'rx':
self.draw_rx(attrs)
@ -78,7 +78,7 @@ class AntennaStat(LineReceiver):
def draw_rx(self, attrs):
p = attrs['packets']
rssi_d = attrs['rssi']
stats_d = attrs['rx_ant_stats']
tx_ant = attrs.get('tx_ant')
rx_id = attrs['id']
@ -101,13 +101,16 @@ class AntennaStat(LineReceiver):
if y < ymax:
addstr(window, y, 0, msg, attr)
if rssi_d:
addstr(window, 0, 25, '[ANT] pkt/s RSSI [dBm]')
for y, (k, v) in enumerate(sorted(rssi_d.items()), 1):
pkt_s, rssi_min, rssi_avg, rssi_max = v
if stats_d:
addstr(window, 0, 24, 'Freq [ANT] pkt/s RSSI [dBm] SNR [dB]')
for y, ((freq, ant_id), v) in enumerate(sorted(stats_d.items()), 1):
pkt_s, rssi_min, rssi_avg, rssi_max, snr_min, snr_avg, snr_max = v
if y < ymax:
active_tx = '*' if (int(k, 16) >> 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))
active_tx = '*' if (ant_id >> 8) == tx_ant else ' '
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:
addstr(window, 0, 25, '[No data]', curses.A_REVERSE)
@ -139,9 +142,10 @@ class AntennaStat(LineReceiver):
if latency_d:
addstr(window, 0, 25, '[ANT] pkt/s Injection [us]')
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
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:
addstr(window, 0, 25, '[No data]', curses.A_REVERSE)

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python
# -*- 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

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python
# -*- 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

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python
# -*- 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

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python
# -*- 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

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python
# -*- 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

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python
# -*- 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
@ -19,7 +19,7 @@
#
import sys
import json
import msgpack
import os
import re
import hashlib
@ -29,7 +29,7 @@ from twisted.python import log, failure
from twisted.python.logfile import LogFile
from twisted.internet import reactor, defer, main as ti_main
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 . import _log_msg, ConsoleObserver, call_and_check_rc, ExecError
@ -57,18 +57,20 @@ class WFBFlags(object):
fec_types = {1: 'VDM_RS'}
class StatisticsProtocol(Protocol):
class StatisticsProtocol(Int32StringReceiver):
MAX_LENGTH = 1024 * 1024
def connectionMade(self):
self.factory.ui_sessions.append(self)
def dataReceived(self, data):
def stringReceived(self, string):
pass
def connectionLost(self, reason):
self.factory.ui_sessions.remove(self)
def send_stats(self, data):
self.transport.write(json.dumps(data).encode('utf-8') + b'\n')
self.sendString(msgpack.packb(data))
class StatsAndSelectorFactory(Factory):
@ -97,11 +99,41 @@ class StatsAndSelectorFactory(Factory):
def add_rssi_cb(self, 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 = {}
for k, grp in groupby(sorted(((int(ant_id, 16) >> 8) & 0xff, rssi_avg) \
for ant_id, (pkt_s, rssi_min, rssi_avg, rssi_max) in ant_rssi.items()),
for k, grp in groupby(sorted(((ant_id >> 8) & 0xff, rssi_avg) \
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]):
# Select max average rssi [dBm] from all wlan's antennas
wlan_rssi[k] = max(rssi for _, rssi in grp)
@ -125,42 +157,43 @@ class StatsAndSelectorFactory(Factory):
self.tx_sel = max_rssi_ant
def update_rx_stats(self, rx_id, packet_stats, ant_rssi):
mav_rssi = []
def update_rx_stats(self, rx_id, packet_stats, ant_stats):
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())):
pkt_s, rssi_min, rssi_avg, rssi_max = v
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 stats_agg and self.ant_sel_cb_list:
self.select_tx_antenna(stats_agg)
if self.rssi_cb_l:
_idx = 0 if settings.common.mavlink_err_rate else 1
flags = 0
if not mav_rssi:
if not card_rssi_l:
flags |= WFBFlags.LINK_LOST
elif packet_stats['dec_err'][0] + packet_stats['bad'][0] > 0:
flags |= WFBFlags.LINK_JAMMED
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)
mav_rssi = (max(card_rssi_l) if card_rssi_l else -128) % 256
for rssi_cb in self.rssi_cb_l:
try:
rssi_cb(rx_id, rssi, rx_errors, rx_fec, flags)
rssi_cb(rx_id, mav_rssi, rx_errors, rx_fec, flags)
except Exception:
log.err()
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
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):
if settings.common.debug:
@ -197,9 +230,9 @@ class RXAntennaProtocol(LineReceiver):
cmd = cols[1]
if cmd == 'RX_ANT':
if len(cols) != 4:
if len(cols) != 5:
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':
if len(cols) != 3:
@ -280,7 +313,7 @@ class TXAntennaProtocol(LineReceiver):
elif cmd == 'TX_ANT':
if len(cols) != 4:
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':
if len(cols) != 3:

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python
# -*- 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