Add support for rx-only cards

This commit is contained in:
Vasily Evseenko 2024-09-26 19:40:17 +03:00
parent 340e699c71
commit b9ba1b69d8
5 changed files with 57 additions and 24 deletions

View File

@ -126,7 +126,7 @@ ip link set {{ wlan }} down
iw dev {{ wlan }} set monitor otherbss
ip link set {{ wlan }} up
iw dev {{ wlan }} set channel {{ channel[wlan] }} {{ ht_mode }}
{% if txpower[wlan] != None %}
{% if txpower[wlan] not in (None, 'off') %}
iw dev {{ wlan }} set txpower fixed {{ txpower[wlan] }}
{% endif %}
{% endfor %}

View File

@ -37,10 +37,11 @@ wifi_channel = 165 # radio channel @5825 MHz, range: 5815-5835 MHz, width
wifi_region = 'BO' # Set CRDA region
wifi_txpower = None # Leave None to use default power settings from driver.
# There is a special value 'off' for RX only cards.
# For 8812au set to -dBm * 100. I.e for 30dBm set to -3000
# For 8812eu set to dBm * 100. I.e for 30dBm set to 3000
# Also you can set own txpower for each wifi card, for example:
# {'wlan0': -100, 'wlan1': 100}
# {'wlan0': -100, 'wlan1': 100, 'wlan2': 'off'}
temp_measurement_interval = 10 # [s] (8812eu only) Internal RF path temp measurement.

View File

@ -112,8 +112,9 @@ class StatsAndSelectorFactory(Factory):
Aggregate RX stats and select TX antenna
"""
def __init__(self, logger, cli_title=None, rf_temp_meter=None, is_cluster=False):
def __init__(self, logger, cli_title=None, rf_temp_meter=None, is_cluster=False, rx_only_wlan_ids=None):
self.is_cluster = is_cluster
self.rx_only_wlan_ids = rx_only_wlan_ids or set()
self.ant_sel_cb_list = []
self.rssi_cb_l = []
self.cur_stats = {}
@ -188,6 +189,10 @@ class StatsAndSelectorFactory(Factory):
snr_min, snr_avg, snr_max) in stats_agg.items()),
lambda x: x[0]):
# Skip RX only cards in TX voting
if wlan_id in self.rx_only_wlan_ids:
continue
grp = list(grp)
# Use max average rssi [dBm] from all wlan's antennas
# Use max packet counter per antenna from all wlan's antennas

View File

@ -22,6 +22,7 @@ import sys
import msgpack
import os
import time
import socket
import struct
import gzip
import argparse
@ -88,13 +89,13 @@ def init_wlans(max_bw, wlans):
yield call_and_check_rc('iw', 'dev', wlan, 'set', 'channel', str(channel), ht_mode)
# You can set own tx power for each card
if isinstance(settings.common.wifi_txpower, dict):
txpower = settings.common.wifi_txpower[wlan]
else:
txpower = settings.common.wifi_txpower
txpower = settings.common.wifi_txpower
if txpower is not None:
# You can set own tx power for each card
if isinstance(txpower, dict):
txpower = txpower[wlan]
if txpower not in (None, 'off'):
yield call_and_check_rc('iw', 'dev', wlan, 'set', 'txpower', 'fixed', str(txpower))
except ExecError as v:
@ -119,8 +120,21 @@ def init(profiles, wlans, cluster_mode):
def _ssh_exited(x, node):
raise Exception('Connection to %s closed, aborting' % (node,))
rx_only_wlan_ids = set()
if is_cluster:
services, cluster_nodes = parse_cluster_services(profiles)
for node in cluster_nodes:
node_ipv4_addr = struct.unpack("!L", socket.inet_aton(node))[0]
txpower = search_attr('wifi_txpower',
settings.cluster.nodes[node],
settings.common.__dict__)
for idx, wlan in enumerate(settings.cluster.nodes[node]['wlans']):
if (txpower[wlan] if isinstance(txpower, dict) else txpower) == 'off':
rx_only_wlan_ids.add((node_ipv4_addr << 24) | idx)
if cluster_mode == 'ssh':
for node, setup_script in gen_cluster_scripts(cluster_nodes, ssh_mode=True).items():
ssh_user = search_attr('ssh_user',
@ -153,6 +167,13 @@ def init(profiles, wlans, cluster_mode):
max_bw = max(cfg.bandwidth for _, tmp in services for _, _, cfg in tmp)
yield init_wlans(max_bw, wlans)
txpower = settings.common.wifi_txpower
for idx, wlan in enumerate(wlans):
if (txpower[wlan] if isinstance(txpower, dict) else txpower) == 'off':
rx_only_wlan_ids.add(idx)
sockets = []
cleanup_l = []
@ -171,6 +192,9 @@ def init(profiles, wlans, cluster_mode):
else:
rf_temp_meter = None
if rx_only_wlan_ids:
log.msg('RX-only wlan ids: %s' % (', '.join(map(hex, rx_only_wlan_ids))))
for profile, service_list in services:
# Domain wide antenna selector
profile_cfg = getattr(settings, profile)
@ -192,7 +216,7 @@ def init(profiles, wlans, cluster_mode):
'cluster' if is_cluster else ', '.join(wlans),
profile_cfg.link_domain)
ant_sel_f = StatsAndSelectorFactory(logger, cli_title, rf_temp_meter, is_cluster)
ant_sel_f = StatsAndSelectorFactory(logger, cli_title, rf_temp_meter, is_cluster, rx_only_wlan_ids)
cleanup_l.append(ant_sel_f)
link_id = hash_link_domain(profile_cfg.link_domain)
@ -204,7 +228,7 @@ def init(profiles, wlans, cluster_mode):
log.msg('Starting %s/%s@%s' % (profile, service_name, profile_cfg.link_domain))
dl.append(defer.maybeDeferred(type_map[service_type], service_name, srv_cfg,
srv_cfg.udp_peers_auto if is_cluster else wlans,
link_id, ant_sel_f, is_cluster))
link_id, ant_sel_f, is_cluster, rx_only_wlan_ids))
yield defer.gatherResults(dl, consumeErrors=True).addBoth(_cleanup).addErrback(lambda f: f.trap(defer.FirstError) and f.value.subFailure)

View File

@ -74,12 +74,12 @@ def parse_services(profile_name, udp_port_allocator):
@defer.inlineCallbacks
def init_udp_direct_tx(service_name, cfg, wlans, link_id, ant_sel_f, is_cluster):
def init_udp_direct_tx(service_name, cfg, wlans, link_id, ant_sel_f, is_cluster, rx_only_wlan_ids):
# Direct udp doesn't support TX diversity - only first card will be used.
# But if mirror mode is enabled it will use all cards.
if not cfg.mirror and (len(wlans) > 1 or ',' in wlans[0]):
raise Exception("udp_direct_tx doesn't supports diversity but multiple cards selected. Use udp_proxy for such case.")
if not cfg.mirror and (len(wlans) > 1 or ',' in wlans[0] or rx_only_wlan_ids):
raise Exception("udp_direct_tx doesn't supports diversity and/or rx-only wlans. Use udp_proxy for such case.")
if not listen_re.match(cfg.peer):
raise Exception('%s: unsupported peer address: %s' % (service_name, cfg.peer))
@ -130,7 +130,7 @@ def init_udp_direct_tx(service_name, cfg, wlans, link_id, ant_sel_f, is_cluster)
yield df
def init_udp_direct_rx(service_name, cfg, wlans, link_id, ant_sel_f, is_cluster):
def init_udp_direct_rx(service_name, cfg, wlans, link_id, ant_sel_f, is_cluster, rx_only_wlan_ids):
if not connect_re.match(cfg.peer):
raise Exception('%s: unsupported peer address: %s' % (service_name, cfg.peer))
@ -156,7 +156,7 @@ def init_udp_direct_rx(service_name, cfg, wlans, link_id, ant_sel_f, is_cluster)
@defer.inlineCallbacks
def init_mavlink(service_name, cfg, wlans, link_id, ant_sel_f, is_cluster):
def init_mavlink(service_name, cfg, wlans, link_id, ant_sel_f, is_cluster, rx_only_wlan_ids):
listen = None
connect = None
serial = None
@ -281,7 +281,7 @@ def init_mavlink(service_name, cfg, wlans, link_id, ant_sel_f, is_cluster):
log.msg('%s use wfb_tx ports %s, control_port %d' % (service_name, tx_ports, control_port))
p_tx_map = dict((wlan_id, UDPProxyProtocol(('127.0.0.1', port))) for wlan_id, port in tx_ports.items())
p_tx_map = dict((wlan_id, UDPProxyProtocol(('127.0.0.1', port))) for wlan_id, port in tx_ports.items() if wlan_id not in rx_only_wlan_ids)
if serial:
serial_port = SerialPort(p_in, os.path.join('/dev', serial[0]), reactor, baudrate=serial[1])
@ -298,7 +298,8 @@ def init_mavlink(service_name, cfg, wlans, link_id, ant_sel_f, is_cluster):
if wlan_id is not None \
else list(p_tx_map.values())[0]
ant_sel_f.add_ant_sel_cb(ant_sel_cb)
if p_tx_map:
ant_sel_f.add_ant_sel_cb(ant_sel_cb)
# Report RSSI to OSD
ant_sel_f.add_rssi_cb(p_in.send_rssi)
@ -320,7 +321,7 @@ def init_mavlink(service_name, cfg, wlans, link_id, ant_sel_f, is_cluster):
@defer.inlineCallbacks
def init_tunnel(service_name, cfg, wlans, link_id, ant_sel_f, is_cluster):
def init_tunnel(service_name, cfg, wlans, link_id, ant_sel_f, is_cluster, rx_only_wlan_ids):
p_in = TUNTAPProtocol(mtu=settings.common.radio_mtu,
agg_timeout=settings.common.tunnel_agg_timeout)
@ -383,7 +384,7 @@ def init_tunnel(service_name, cfg, wlans, link_id, ant_sel_f, is_cluster):
control_port = yield control_port_df
log.msg('%s use wfb_tx ports %s, control_port %d' % (service_name, tx_ports, control_port))
p_tx_map = dict((wlan_id, UDPProxyProtocol(('127.0.0.1', port))) for wlan_id, port in tx_ports.items())
p_tx_map = dict((wlan_id, UDPProxyProtocol(('127.0.0.1', port))) for wlan_id, port in tx_ports.items() if wlan_id not in rx_only_wlan_ids)
tun_ep = TUNTAPTransport(reactor, p_in, cfg.ifname, cfg.ifaddr, mtu=settings.common.radio_mtu, default_route=cfg.default_route)
sockets += [ reactor.listenUDP(0, p_tx) for p_tx in p_tx_map.values() ]
@ -402,7 +403,8 @@ def init_tunnel(service_name, cfg, wlans, link_id, ant_sel_f, is_cluster):
else:
p_in.all_peers = list(p_tx_map.values())
ant_sel_f.add_ant_sel_cb(ant_sel_cb)
if p_tx_map:
ant_sel_f.add_ant_sel_cb(ant_sel_cb)
dl.append(RXProtocol(ant_sel_f, cmd_rx, '%s rx' % (service_name,)).start())
@ -417,7 +419,7 @@ def init_tunnel(service_name, cfg, wlans, link_id, ant_sel_f, is_cluster):
@defer.inlineCallbacks
def init_udp_proxy(service_name, cfg, wlans, link_id, ant_sel_f, is_cluster):
def init_udp_proxy(service_name, cfg, wlans, link_id, ant_sel_f, is_cluster, rx_only_wlan_ids):
listen = None
connect = None
@ -500,7 +502,7 @@ def init_udp_proxy(service_name, cfg, wlans, link_id, ant_sel_f, is_cluster):
log.msg('%s use wfb_tx ports %s, control_port %d' % (service_name, tx_ports, control_port))
p_tx_map = dict((wlan_id, UDPProxyProtocol(('127.0.0.1', port))) for wlan_id, port in tx_ports.items())
p_tx_map = dict((wlan_id, UDPProxyProtocol(('127.0.0.1', port))) for wlan_id, port in tx_ports.items() if wlan_id not in rx_only_wlan_ids)
sockets += [ reactor.listenUDP(0, p_tx) for p_tx in p_tx_map.values() ]
def ant_sel_cb(wlan_id):
@ -508,7 +510,8 @@ def init_udp_proxy(service_name, cfg, wlans, link_id, ant_sel_f, is_cluster):
if wlan_id is not None \
else list(p_tx_map.values())[0]
ant_sel_f.add_ant_sel_cb(ant_sel_cb)
if p_tx_map:
ant_sel_f.add_ant_sel_cb(ant_sel_cb)
def _cleanup(x):
for s in sockets: