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
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)
[802.11 timings](https://github.com/ewa/802.11-data)
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]
Type=simple
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
Restart=on-failure
RestartSec=5s

View File

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

View File

@ -135,7 +135,7 @@ def init(stdscr, profile):
i.scrollok(1)
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:
status_win1.addstr(0, 0, '[statistics disabled]', curses.A_REVERSE)
status_win1.refresh()

View File

@ -3,6 +3,7 @@ conf_dir = '/etc'
bin_dir = '/usr/bin'
tmp_dir = '/tmp'
[common]
debug = False
version = '0.0.1.trunk'
@ -11,6 +12,9 @@ commit = None
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
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]
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
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
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
# Radio settings for TX and RX
bandwidth = 20 # bandwidth 20 or 40 MHz
# Radiotap flags for TX:
bandwidth = 40 # bandwidth 20 or 40 MHz
short_gi = True # use short GI or not
short_gi = False # use short GI or not
stbc = 1 # stbc streams: 1, 2, 3 or 0 if unused
mcs_index = 1 # mcs index
[gs_video]
keypair = 'gs.key' # keypair generated by wfb-keygen
stats_port = 8002 # used by wfb-cli
stream = 3 # radio port for video stream
listen = None # udp port for video source (drone) or None for GS
connect = 5600 # udp port for video sink (GS) or None for drone
peer = 'connect://127.0.0.1:5600' # outgoing connection for video sink (GS)
# Radio settings for RX
bandwidth = 20 # bandwidth 20 or 40 MHz
[drone_mavlink]
keypair = 'drone.key'
@ -42,23 +55,31 @@ stream_tx = 2
stream_rx = 1
port_rx = 14700
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
# Radio settings for TX and RX
bandwidth = 20 # bandwidth 20 or 40 MHz
# Radiotap flags for TX:
bandwidth = 40 # bandwidth 20 or 40 MHz
short_gi = True # use short GI or not
short_gi = False # use short GI or not
stbc = 1 # stbc streams: 1, 2, 3 or 0 if unused
mcs_index = 1 # mcs index
[drone_video]
keypair = 'drone.key'
stats_port = None
stream = 3
listen = 5602
connect = None
peer = 'listen://0.0.0.0:5602' # listen for video stream (drone)
# Radio settings for TX and RX
bandwidth = 20 # bandwidth 20 or 40 MHz
# Radiotap flags for TX:
bandwidth = 40 # bandwidth 20 or 40 MHz
short_gi = True # use short GI or not
short_gi = False # use short GI or not
stbc = 1 # stbc streams: 1, 2, 3 or 0 if unused
mcs_index = 1 # mcs index

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python
# -*- 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
@ -22,10 +22,11 @@ import sys
import time
import json
import os
import re
from itertools import groupby
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.protocols.basic import LineReceiver
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.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):
pass
@ -242,10 +273,38 @@ class TXProtocol(ProcessProtocol):
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_services(_):
return defer.gatherResults([defer.maybeDeferred(init_mavlink, profile, wlans),
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):
@ -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.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
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:
connect = ('127.0.0.1', cfg.connect)
listen = 0
raise Exception('Unsupport peer address: %s' % (cfg.peer,))
# The first argument is not None only if we initiate mavlink connection
p_in = UDPProxyProtocol(connect, agg_max_size=settings.common.radio_mtu,
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_rx = UDPProxyProtocol()
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 ]
log.msg('Telem RX: %s' % (' '.join(cmd_rx),))
@ -296,33 +363,44 @@ def init_mavlink(profile, wlans):
def init_video(profile, wlans):
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
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,
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,
wlans[0])).split()
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)
if cfg.stats_port:
reactor.listenTCP(cfg.stats_port, ant_f)
cmd = ('%s -p %d -u %d -K %s' % \
(os.path.join(settings.path.bin_dir, 'wfb_rx'), cfg.stream, cfg.connect,
cmd = ('%s -p %d -c %s -u %d -K %s' % \
(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
df = RXProtocol(ant_f, cmd, 'video rx').start()
else:
raise Exception('Unsupport peer address: %s' % (cfg.peer,))
log.msg('Video: %s' % (' '.join(cmd),))
return df
def main():
log.startLogging(sys.stdout)
reactor.callWhenRunning(lambda: defer.maybeDeferred(init, sys.argv[1], sys.argv[2:])\
profile, wlans = sys.argv[1], sys.argv[2:]
reactor.callWhenRunning(lambda: defer.maybeDeferred(init, profile, wlans)\
.addErrback(abort_on_crash))
reactor.run()