Change connect/listen field format in config. Move wlan radio init to python

This commit is contained in:
Vasily Evseenko 2019-01-10 14:41:06 +03:00
parent 1936713c81
commit 97a1274581
8 changed files with 133 additions and 59 deletions

View File

@ -36,7 +36,7 @@ A: Wifibroadcast is not tied to any GPU - it operates with UDP packets. But to g
## Theory ## Theory
Wifibroadcast puts the wifi cards into monitor mode. This mode allows to send and receive arbitrary packets without association and waiting for ACK packets. Wifibroadcast puts the wifi cards into monitor mode. This mode allows to send and receive arbitrary packets without association and waiting for ACK packets.
[Analysis of Injection Capabilities and Media Access of IEEE 802.11 Hardware in Monitor Mode](https://github.com/svpcom/wifibroadcast/blob/master/patches/Analysis%20of%20Injection%20Capabilities%20and%20Media%20Access%20of%20IEEE%20802.11%20Hardware%20in%20Monitor%20Mode.pdf) [Analysis of Injection Capabilities and Media Access of IEEE 802.11 Hardware in Monitor Mode](https://github.com/svpcom/wifibroadcast/blob/master/patches/Analysis%20of%20Injection%20Capabilities%20and%20Media%20Access%20of%20IEEE%20802.11%20Hardware%20in%20Monitor%20Mode.pdf)
[802.11 timings](https://github.com/ewa/802.11-data)
Sample usage chain: Sample usage chain:
------------------- -------------------
``` ```

Binary file not shown.

View File

@ -1,24 +0,0 @@
#!/bin/bash
set -e
if [ $# -lt 2 ]
then echo "Usage: $0 <profile> <wlan1> [wlan2] ..."
exit 1
fi
PROFILE=$1
shift 1
WLANS=$@
CHANNEL5G="149" # Freq: 5805 (57955815) BW 40 MHz
for WLAN in $WLANS
do
echo "Setting $WLAN to channel $CHANNEL5G"
ifconfig $WLAN down
iw dev $WLAN set monitor otherbss
iw reg set BO
ifconfig $WLAN up
iw dev $WLAN set channel $CHANNEL5G HT40+
done
exec python -m telemetry.server $PROFILE $WLANS

View File

@ -6,7 +6,7 @@ ReloadPropagatedFrom=wifibroadcast.service
[Service] [Service]
Type=simple Type=simple
EnvironmentFile=/etc/default/wifibroadcast EnvironmentFile=/etc/default/wifibroadcast
ExecStart=/usr/bin/wfb-server.sh %i ${WFB_NICS} ExecStart=/usr/bin/python -m telemetry.server %i ${WFB_NICS}
TimeoutStopSec=5s TimeoutStopSec=5s
Restart=on-failure Restart=on-failure
RestartSec=5s RestartSec=5s

View File

@ -66,7 +66,6 @@ setup(
packages=find_packages(exclude=["*.tests", "*.tests.*", "tests.*", "tests"]), packages=find_packages(exclude=["*.tests", "*.tests.*", "tests.*", "tests"]),
zip_safe=False, zip_safe=False,
entry_points={'console_scripts': ['wfb-cli=telemetry.cli:main']}, entry_points={'console_scripts': ['wfb-cli=telemetry.cli:main']},
scripts=['scripts/wfb-server.sh'],
package_data={'telemetry.conf': ['master.cfg', 'site.cfg']}, package_data={'telemetry.conf': ['master.cfg', 'site.cfg']},
data_files = [('/usr/bin', ['wfb_tx', 'wfb_rx', 'wfb_keygen']), data_files = [('/usr/bin', ['wfb_tx', 'wfb_rx', 'wfb_keygen']),
('/lib/systemd/system', ['scripts/wifibroadcast.service', ('/lib/systemd/system', ['scripts/wifibroadcast.service',

View File

@ -135,7 +135,7 @@ def init(stdscr, profile):
i.scrollok(1) i.scrollok(1)
if cfg_video.stats_port is not None: if cfg_video.stats_port is not None:
reactor.connectTCP('127.0.0.1', cfg_video.stats_port, AntennaStatClientFactory(status_win1, cfg_video.listen is not None)) reactor.connectTCP('127.0.0.1', cfg_video.stats_port, AntennaStatClientFactory(status_win1, cfg_video.peer.startswith('listen:')))
else: else:
status_win1.addstr(0, 0, '[statistics disabled]', curses.A_REVERSE) status_win1.addstr(0, 0, '[statistics disabled]', curses.A_REVERSE)
status_win1.refresh() status_win1.refresh()

View File

@ -3,6 +3,7 @@ conf_dir = '/etc'
bin_dir = '/usr/bin' bin_dir = '/usr/bin'
tmp_dir = '/tmp' tmp_dir = '/tmp'
[common] [common]
debug = False debug = False
version = '0.0.1.trunk' version = '0.0.1.trunk'
@ -11,6 +12,9 @@ commit = None
radio_mtu = 1446 # MAX_PAYLOAD_SIZE, don't change if doubt radio_mtu = 1446 # MAX_PAYLOAD_SIZE, don't change if doubt
mavlink_agg_timeout = 0.1 # aggragate mavlink packets if less than radio_mtu but no longer than 100ms mavlink_agg_timeout = 0.1 # aggragate mavlink packets if less than radio_mtu but no longer than 100ms
tx_sel_delta = 3 # hysteresis for antenna selection, [dB] tx_sel_delta = 3 # hysteresis for antenna selection, [dB]
wifi_channel = 165 # radio channel @5825 MHz, range: 58155835 MHz, width 20MHz
wifi_region = 'BO' # Set CRDA region
[gs_mavlink] [gs_mavlink]
keypair = 'gs.key' # keypair generated by wfb-keygen keypair = 'gs.key' # keypair generated by wfb-keygen
@ -19,21 +23,30 @@ stream_tx = 1 # radio port for mavlink tx
stream_rx = 2 # radio port for mavlink rx stream_rx = 2 # radio port for mavlink rx
port_rx = 14600 # udp port for internal use port_rx = 14600 # udp port for internal use
port_tx = 14601 # udp port range (from port_tx to port_tx + number of wlans) for internal use port_tx = 14601 # udp port range (from port_tx to port_tx + number of wlans) for internal use
listen = None # udp port for incoming connection, conflicts with 'connect'
connect = 14550 # udp port for outgoing connection, conflicts with 'listen' peer = 'connect://127.0.0.1:14550' # outgoing connection
# peer = 'listen://0.0.0.0:14550' # incoming connection
inject_rssi = True # inject RADIO_STATUS packets inject_rssi = True # inject RADIO_STATUS packets
# Radio settings for TX and RX
bandwidth = 20 # bandwidth 20 or 40 MHz
# Radiotap flags for TX: # Radiotap flags for TX:
bandwidth = 40 # bandwidth 20 or 40 MHz short_gi = False # use short GI or not
short_gi = True # use short GI or not
stbc = 1 # stbc streams: 1, 2, 3 or 0 if unused stbc = 1 # stbc streams: 1, 2, 3 or 0 if unused
mcs_index = 1 # mcs index mcs_index = 1 # mcs index
[gs_video] [gs_video]
keypair = 'gs.key' # keypair generated by wfb-keygen keypair = 'gs.key' # keypair generated by wfb-keygen
stats_port = 8002 # used by wfb-cli stats_port = 8002 # used by wfb-cli
stream = 3 # radio port for video stream stream = 3 # radio port for video stream
listen = None # udp port for video source (drone) or None for GS peer = 'connect://127.0.0.1:5600' # outgoing connection for video sink (GS)
connect = 5600 # udp port for video sink (GS) or None for drone
# Radio settings for RX
bandwidth = 20 # bandwidth 20 or 40 MHz
[drone_mavlink] [drone_mavlink]
keypair = 'drone.key' keypair = 'drone.key'
@ -42,23 +55,31 @@ stream_tx = 2
stream_rx = 1 stream_rx = 1
port_rx = 14700 port_rx = 14700
port_tx = 14701 port_tx = 14701
listen = None
connect = 14560 peer = 'listen://0.0.0.0:14560' # incoming connection
#peer = 'connect://127.0.0.1:14560' # outgoing connection
inject_rssi = True # inject RADIO_STATUS packets inject_rssi = True # inject RADIO_STATUS packets
# Radio settings for TX and RX
bandwidth = 20 # bandwidth 20 or 40 MHz
# Radiotap flags for TX: # Radiotap flags for TX:
bandwidth = 40 # bandwidth 20 or 40 MHz short_gi = False # use short GI or not
short_gi = True # use short GI or not
stbc = 1 # stbc streams: 1, 2, 3 or 0 if unused stbc = 1 # stbc streams: 1, 2, 3 or 0 if unused
mcs_index = 1 # mcs index mcs_index = 1 # mcs index
[drone_video] [drone_video]
keypair = 'drone.key' keypair = 'drone.key'
stats_port = None stats_port = None
stream = 3 stream = 3
listen = 5602 peer = 'listen://0.0.0.0:5602' # listen for video stream (drone)
connect = None
# Radio settings for TX and RX
bandwidth = 20 # bandwidth 20 or 40 MHz
# Radiotap flags for TX: # Radiotap flags for TX:
bandwidth = 40 # bandwidth 20 or 40 MHz short_gi = False # use short GI or not
short_gi = True # use short GI or not
stbc = 1 # stbc streams: 1, 2, 3 or 0 if unused stbc = 1 # stbc streams: 1, 2, 3 or 0 if unused
mcs_index = 1 # mcs index mcs_index = 1 # mcs index

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (C) 2018 Vasily Evseenko <svpcom@p2ptech.org> # Copyright (C) 2018, 2019 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
@ -22,10 +22,11 @@ import sys
import time import time
import json import json
import os import os
import re
from itertools import groupby from itertools import groupby
from twisted.python import log from twisted.python import log
from twisted.internet import reactor, defer from twisted.internet import reactor, defer, utils
from twisted.internet.protocol import ProcessProtocol, DatagramProtocol, Protocol, Factory from twisted.internet.protocol import ProcessProtocol, DatagramProtocol, Protocol, Factory
from twisted.protocols.basic import LineReceiver from twisted.protocols.basic import LineReceiver
from twisted.internet.error import ReactorNotRunning from twisted.internet.error import ReactorNotRunning
@ -34,6 +35,36 @@ from telemetry.common import abort_on_crash, exit_status
from telemetry.proxy import UDPProxyProtocol from telemetry.proxy import UDPProxyProtocol
from telemetry.conf import settings from telemetry.conf import settings
connect_re = re.compile(r'^connect://(?P<addr>[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+):(?P<port>[0-9]+)$', re.IGNORECASE)
listen_re = re.compile(r'^listen://(?P<addr>[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+):(?P<port>[0-9]+)$', re.IGNORECASE)
class ExecError(Exception):
pass
def call_and_check_rc(cmd, *args):
def _check_rc((stdout, stderr, rc)):
if rc != 0:
err = ExecError('RC %d: %s %s' % (rc, cmd, ' '.join(args)))
err.stdout = stdout.strip()
err.stderr = stderr.strip()
raise err
log.msg('# %s' % (' '.join((cmd,) + args),))
if stdout:
log.msg(stdout)
def _got_signal(f):
f.trap(tuple)
stdout, stderr, signum = f.value
err = ExecError('Got signal %d: %s %s' % (signum, cmd, ' '.join(args)))
err.stdout = stdout.strip()
err.stderr = stderr.strip()
raise err
return utils.getProcessOutputAndValue(cmd, args, env=os.environ).addCallbacks(_check_rc, _got_signal)
class BadTelemetry(Exception): class BadTelemetry(Exception):
pass pass
@ -242,10 +273,38 @@ class TXProtocol(ProcessProtocol):
return df.addCallback(lambda _: self.df) return df.addCallback(lambda _: self.df)
@defer.inlineCallbacks
def init_wlans(profile, wlans):
max_bw = max(getattr(getattr(settings, '%s_mavlink' % profile), 'bandwidth'),
getattr(getattr(settings, '%s_video' % profile), 'bandwidth'))
if max_bw == 20:
ht_mode = 'HT20'
elif max_bw == 40:
ht_mode = 'HT40+'
else:
raise Exception('Unsupported bandwith %d MHz' % (max_bw,))
try:
yield call_and_check_rc('iw', 'reg', 'set', settings.common.wifi_region)
for wlan in wlans:
yield call_and_check_rc('ifconfig', wlan, 'down')
yield call_and_check_rc('iw', 'dev', wlan, 'set', 'monitor', 'otherbss')
yield call_and_check_rc('ifconfig', wlan, 'up')
yield call_and_check_rc('iw', 'dev', wlan, 'set', 'channel', str(settings.common.wifi_channel), ht_mode)
except ExecError as v:
if v.stdout:
log.msg(v.stdout, isError=1)
if v.stderr:
log.msg(v.stderr, isError=1)
raise
def init(profile, wlans): def init(profile, wlans):
return defer.gatherResults([defer.maybeDeferred(init_mavlink, profile, wlans), def _init_services(_):
defer.maybeDeferred(init_video, profile, wlans)])\ return defer.gatherResults([defer.maybeDeferred(init_mavlink, profile, wlans),
.addErrback(lambda f: f.trap(defer.FirstError) and f.value.subFailure) defer.maybeDeferred(init_video, profile, wlans)])\
.addErrback(lambda f: f.trap(defer.FirstError) and f.value.subFailure)
return init_wlans(profile, wlans).addCallback(_init_services)
def init_mavlink(profile, wlans): def init_mavlink(profile, wlans):
@ -260,19 +319,27 @@ def init_mavlink(profile, wlans):
cfg.stream_tx, cfg.port_tx, os.path.join(settings.path.conf_dir, cfg.keypair), cfg.stream_tx, cfg.port_tx, os.path.join(settings.path.conf_dir, cfg.keypair),
cfg.bandwidth, "short" if cfg.short_gi else "long", cfg.stbc, cfg.mcs_index)).split() + wlans cfg.bandwidth, "short" if cfg.short_gi else "long", cfg.stbc, cfg.mcs_index)).split() + wlans
if cfg.listen: if connect_re.match(cfg.peer):
m = connect_re.match(cfg.peer)
connect = m.group('addr'), int(m.group('port'))
listen = None
log.msg('Connect telem stream %d(RX), %d(TX) to %s:%d' % (cfg.stream_rx, cfg.stream_tx, connect[0], connect[1]))
elif listen_re.match(cfg.peer):
m = listen_re.match(cfg.peer)
listen = m.group('addr'), int(m.group('port'))
connect = None connect = None
listen = cfg.listen log.msg('Listen for telem stream %d(RX), %d(TX) on %s:%d' % (cfg.stream_rx, cfg.stream_tx, listen[0], listen[1]))
else: else:
connect = ('127.0.0.1', cfg.connect) raise Exception('Unsupport peer address: %s' % (cfg.peer,))
listen = 0
# The first argument is not None only if we initiate mavlink connection
p_in = UDPProxyProtocol(connect, agg_max_size=settings.common.radio_mtu, p_in = UDPProxyProtocol(connect, agg_max_size=settings.common.radio_mtu,
agg_timeout=settings.common.mavlink_agg_timeout, inject_rssi=cfg.inject_rssi) agg_timeout=settings.common.mavlink_agg_timeout, inject_rssi=cfg.inject_rssi)
p_tx_l = [UDPProxyProtocol(('127.0.0.1', cfg.port_tx + i)) for i, _ in enumerate(wlans)] p_tx_l = [UDPProxyProtocol(('127.0.0.1', cfg.port_tx + i)) for i, _ in enumerate(wlans)]
p_rx = UDPProxyProtocol() p_rx = UDPProxyProtocol()
p_rx.peer = p_in p_rx.peer = p_in
sockets = [ reactor.listenUDP(listen, p_in), reactor.listenUDP(cfg.port_rx, p_rx) ] sockets = [ reactor.listenUDP(listen[1] if listen else 0, p_in),
reactor.listenUDP(cfg.port_rx, p_rx) ]
sockets += [ reactor.listenUDP(0, p_tx) for p_tx in p_tx_l ] sockets += [ reactor.listenUDP(0, p_tx) for p_tx in p_tx_l ]
log.msg('Telem RX: %s' % (' '.join(cmd_rx),)) log.msg('Telem RX: %s' % (' '.join(cmd_rx),))
@ -296,33 +363,44 @@ def init_mavlink(profile, wlans):
def init_video(profile, wlans): def init_video(profile, wlans):
cfg = getattr(settings, '%s_video' % (profile,)) cfg = getattr(settings, '%s_video' % (profile,))
if cfg.listen: if listen_re.match(cfg.peer):
m = listen_re.match(cfg.peer)
listen = m.group('addr'), int(m.group('port'))
log.msg('Listen for video stream %d on %s:%d' % (cfg.stream, listen[0], listen[1]))
# We don't use TX diversity for video streaming due to only one transmitter on the vehichle # We don't use TX diversity for video streaming due to only one transmitter on the vehichle
cmd = ('%s -p %d -u %d -K %s -B %d -G %s -S %d -M %d %s' % \ cmd = ('%s -p %d -u %d -K %s -B %d -G %s -S %d -M %d %s' % \
(os.path.join(settings.path.bin_dir, 'wfb_tx'), cfg.stream, (os.path.join(settings.path.bin_dir, 'wfb_tx'), cfg.stream,
cfg.listen, os.path.join(settings.path.conf_dir, cfg.keypair), listen[1], os.path.join(settings.path.conf_dir, cfg.keypair),
cfg.bandwidth, "short" if cfg.short_gi else "long", cfg.stbc, cfg.mcs_index, cfg.bandwidth, "short" if cfg.short_gi else "long", cfg.stbc, cfg.mcs_index,
wlans[0])).split() wlans[0])).split()
df = TXProtocol(cmd, 'video tx').start() df = TXProtocol(cmd, 'video tx').start()
else: elif connect_re.match(cfg.peer):
m = connect_re.match(cfg.peer)
connect = m.group('addr'), int(m.group('port'))
log.msg('Send video stream %d to %s:%d' % (cfg.stream, connect[0], connect[1]))
ant_f = AntennaFactory(None, None) ant_f = AntennaFactory(None, None)
if cfg.stats_port: if cfg.stats_port:
reactor.listenTCP(cfg.stats_port, ant_f) reactor.listenTCP(cfg.stats_port, ant_f)
cmd = ('%s -p %d -u %d -K %s' % \ cmd = ('%s -p %d -c %s -u %d -K %s' % \
(os.path.join(settings.path.bin_dir, 'wfb_rx'), cfg.stream, cfg.connect, (os.path.join(settings.path.bin_dir, 'wfb_rx'),
cfg.stream, connect[0], connect[1],
os.path.join(settings.path.conf_dir, cfg.keypair))).split() + wlans os.path.join(settings.path.conf_dir, cfg.keypair))).split() + wlans
df = RXProtocol(ant_f, cmd, 'video rx').start() df = RXProtocol(ant_f, cmd, 'video rx').start()
else:
raise Exception('Unsupport peer address: %s' % (cfg.peer,))
log.msg('Video: %s' % (' '.join(cmd),)) log.msg('Video: %s' % (' '.join(cmd),))
return df return df
def main(): def main():
log.startLogging(sys.stdout) log.startLogging(sys.stdout)
profile, wlans = sys.argv[1], sys.argv[2:]
reactor.callWhenRunning(lambda: defer.maybeDeferred(init, sys.argv[1], sys.argv[2:])\ reactor.callWhenRunning(lambda: defer.maybeDeferred(init, profile, wlans)\
.addErrback(abort_on_crash)) .addErrback(abort_on_crash))
reactor.run() reactor.run()