├── .clang-format ├── .github └── workflows │ ├── build_and_unit_test.yml │ └── clang_format_test.yml ├── .gitignore ├── CMakeLists.txt ├── CMakePresets.json ├── LICENSE.txt ├── README.md ├── ThroughputBenchmarks ├── fec │ ├── Pi2.txt │ ├── Results.txt │ ├── cm4.txt │ ├── cm4_underclocked.txt │ ├── cm4_underclocked_with_vs_without_neon.txt │ ├── intel.txt │ ├── other.txt │ ├── pi0_2.txt │ ├── pi4.txt │ └── rv1126.txt └── injection │ └── rtl8812au_asus_20mhz_40mhz.txt ├── build_cmake.sh ├── example_key ├── README.md ├── generate.sh └── txrx.key ├── install_dep.sh ├── latencyTesting ├── Readme.md ├── SimpleTestProgram │ ├── .gitignore │ ├── Makefile │ ├── README.md │ ├── UDPReceiver.cpp │ ├── UDPReceiver.h │ ├── UDPSender.cpp │ ├── UDPSender.h │ ├── test.cpp │ ├── udp_forwarder │ └── udp_forwarder.cpp ├── TestResults │ ├── Test_unidirectional_vs_bidirectional.txt │ ├── Test_unidirectional_vs_bidirectional2.txt │ ├── test1 │ │ ├── results.txt │ │ └── startTxAndRxOnMyPc.sh │ ├── test2 │ │ └── results.txt │ ├── test_localhost_ground_and_air.txt │ └── test_wfb_tl_vs_alfa.txt ├── XstartTxSimple.sh ├── YstartRxSimple.sh ├── enable_monitor_mode.sh ├── startRxSimple.sh ├── startRxVideo.sh ├── startTxAndRxOnMyPc.sh ├── startTxAndRxOnMyPc2.sh ├── startTxAndRxOnMyPcInBidirectionalMode.sh ├── startTxSimple.sh ├── startTxVideo.sh └── test_2_cards.sh ├── other └── neon_compiler_test │ ├── Makefile │ └── example.cpp ├── papers ├── Analysis of Injection Capabilities and Media Access of IEEE 802.11 Hardware in Monitor Mode.pdf ├── CollectionOfLinks.txt ├── ExcerptsFromTelegram.txt ├── fec │ ├── 10.1.1.219.3226.pdf │ ├── 10.1.1.80.3513.pdf │ └── NetCod2014-GuRiUt-EfficientGfArithmetics.pdf └── mimo_for_dummies.pdf ├── run_clang_format.sh ├── rv1126 ├── enable_monitor_mode.sh ├── start_rx_simple.sh └── start_tx_simple.sh ├── scripts ├── enable_monitor_mode.sh ├── set_freq.sh ├── simple_2G.sh ├── simple_5G.sh └── simple_consti_pc.sh └── wifibroadcast ├── WBLib.cmake ├── cmake ├── FindPCAP.cmake └── FindSodium.cmake ├── executables ├── benchmark.cpp ├── example_hello.cpp ├── example_pollute.cpp ├── example_udp.cpp ├── injection_rate_test.cpp ├── socket_helper_test.cpp ├── test_dummy_link.cpp ├── test_listen.cpp ├── test_queue.cpp ├── test_txrx.cpp ├── udp_generator_validator.cpp ├── udp_packet_drop_util.cpp ├── unit_test.cpp └── wfb_keygen.cpp ├── lib ├── Readme.txt ├── fec │ ├── fec_base.cpp │ ├── fec_base.h │ ├── gf_optimized │ │ ├── Readme.md │ │ ├── alignment_check.h │ │ ├── gf256_avx2.h │ │ ├── gf256_flat_table.h │ │ ├── gf256_neon.h │ │ ├── gf256_optimized_include.h │ │ ├── gf256_ssse3.h │ │ └── gf256tables285.h │ └── gf_simple │ │ └── gf_simple.h ├── moodycamel │ ├── concurrentqueue │ │ ├── blockingconcurrentqueue.h │ │ ├── concurrentqueue.h │ │ └── lightweightsemaphore.h │ └── readerwriterqueue │ │ ├── atomicops.h │ │ └── readerwritercircularbuffer.h └── radiotap │ ├── platform.h │ ├── radiotap.c │ ├── radiotap.h │ └── radiotap_iter.h └── src ├── FunkyQueue.cpp ├── FunkyQueue.h ├── HelperSources ├── Benchmark.hpp ├── BlockSizeHelper.hpp ├── DummyStreamGenerator.hpp ├── EmulatedPacketDrop.hpp ├── Helper.hpp ├── README.md ├── RandomBufferPot.hpp ├── Rates.hpp ├── SchedulingHelper.hpp ├── SequenceNumberDebugger.hpp ├── SocketHelper.hpp ├── StringHelper.hpp ├── TimeHelper.hpp ├── UINT16SeqNrHelper.hpp └── UINT64SeqNrHelper.hpp ├── Ieee80211Header.hpp ├── SimpleStream.hpp ├── WBStreamRx.cpp ├── WBStreamRx.h ├── WBStreamTx.cpp ├── WBStreamTx.h ├── WBTxRx.cpp ├── WBTxRx.h ├── WBVideoStreamTx.cpp ├── WBVideoStreamTx.h ├── WiFiCard.h ├── dummy_link ├── DummyLink.cpp └── DummyLink.h ├── encryption ├── Decryptor.cpp ├── Decryptor.h ├── Encryption.cpp ├── Encryption.h ├── EncryptionFsUtils.cpp ├── EncryptionFsUtils.h ├── Encryptor.cpp ├── Encryptor.h ├── KeyPair.cpp └── KeyPair.h ├── fec ├── FEC.cpp ├── FEC.h ├── FECConstants.hpp ├── FECDecoder.cpp ├── FECDecoder.h ├── FECEncoder.cpp ├── FECEncoder.h ├── FECPayloadHdr.hpp ├── RxBlock.cpp └── RxBlock.h ├── legacy ├── README.md ├── WBStreamRxUDP.h └── WBStreamTxUDP.h ├── pcap_helper.hpp ├── radiotap ├── README.md ├── RSSIAccumulator.hpp ├── RadiotapHeaderRx.hpp ├── RadiotapHeaderTx.hpp ├── RadiotapHeaderTxHolder.hpp ├── RadiotapRxRfAggregator.cpp ├── RadiotapRxRfAggregator.h ├── SignalQualityAccumulator.hpp └── radiotap_util.hpp ├── raw_socket_helper.hpp ├── tmp.txt ├── wifibroadcast_spdlog.cpp └── wifibroadcast_spdlog.h /.clang-format: -------------------------------------------------------------------------------- 1 | # We use Google c++ style for OpenHD code. 2 | # Any contributions should run clang-format before opening a pull request. 3 | BasedOnStyle: Google 4 | Language: Cpp 5 | Standard: c++17 -------------------------------------------------------------------------------- /.github/workflows/build_and_unit_test.yml: -------------------------------------------------------------------------------- 1 | name: X86 build and run unit test 2 | 3 | on: [push] 4 | 5 | env: 6 | BUILD_TYPE: Release 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-24.04 11 | 12 | steps: 13 | - uses: actions/checkout@v4 14 | 15 | - name: Install Dependencies 16 | run: | 17 | sudo ./install_dep.sh 18 | - name: Build 19 | run: | 20 | ./build_cmake.sh 21 | - name: Test generate keys 22 | run: | 23 | ./build/wfb_keygen 24 | - name: Unit test 1 25 | run: | 26 | cd build 27 | ./unit_test 28 | - name: Unit test 2 29 | run: | 30 | cd build 31 | ./test_queue 32 | - name: Unit test 3 33 | run: | 34 | cd build 35 | sudo ./test_dummy_link 36 | 37 | -------------------------------------------------------------------------------- /.github/workflows/clang_format_test.yml: -------------------------------------------------------------------------------- 1 | # Run clang-tidy 2 | name: Clang-tidy 3 | 4 | on: 5 | push: 6 | 7 | jobs: 8 | 9 | validate_codestyle_clang_format: 10 | runs-on: ubuntu-24.04 11 | steps: 12 | - name: Checkout repository and submodules 13 | uses: actions/checkout@v4 14 | with: 15 | submodules: recursive 16 | 17 | - name: Install Dependencies 18 | run: | 19 | sudo apt install clang-format 20 | 21 | - name: Validate 22 | run: | 23 | ./run_clang_format.sh 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.a 3 | *.pyc 4 | dist/ 5 | deb_dist/ 6 | cmake-build-createDebug/ 7 | cmake-build-debug 8 | build_clang_format 9 | env/ 10 | _trial_temp/ 11 | wfb_rx 12 | wfb_tx 13 | unit_test 14 | benchmark 15 | gs.key 16 | key_1.key 17 | wfb_keygen 18 | udp_generator_validator 19 | socket_helper_test 20 | telemetry/conf/site.cfg 21 | telemetry/conf/local.cfg 22 | build/* 23 | 24 | ### JetBrains template 25 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 26 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 27 | .idea 28 | 29 | # User-specific stuff: 30 | .idea/workspace.xml 31 | .idea/tasks.xml 32 | .idea/dictionaries 33 | .idea/vcs.xml 34 | .idea/jsLibraryMappings.xml 35 | 36 | # Sensitive or high-churn files: 37 | .idea/dataSources.ids 38 | .idea/dataSources.xml 39 | .idea/dataSources.local.xml 40 | .idea/sqlDataSources.xml 41 | .idea/dynamic.xml 42 | .idea/uiDesigner.xml 43 | 44 | # Gradle: 45 | .idea/gradle.xml 46 | .idea/libraries 47 | 48 | # Mongo Explorer plugin: 49 | .idea/mongoSettings.xml 50 | 51 | ## File-based project format: 52 | *.iws 53 | 54 | ## Plugin-specific files: 55 | 56 | # IntelliJ 57 | /out/ 58 | 59 | # mpeltonen/sbt-idea plugin 60 | .idea_modules/ 61 | 62 | # JIRA plugin 63 | atlassian-ide-plugin.xml 64 | 65 | # Crashlytics plugin (for Android Studio and IntelliJ) 66 | com_crashlytics_export_strings.xml 67 | crashlytics.properties 68 | crashlytics-build.properties 69 | fabric.properties 70 | 71 | misc.xml 72 | .idea/* 73 | 74 | /.vs 75 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16.3) 2 | project(Wifibroadcast) 3 | 4 | cmake_minimum_required(VERSION 3.16.3) 5 | set(CMAKE_CXX_STANDARD 20) 6 | #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize-address-use-after-scope -fsanitize=address") 7 | 8 | # Get spdlog from package manager for those tests 9 | set(WB_USE_SPDLOG_EXTERNALLY OFF) 10 | include(wifibroadcast/WBLib.cmake) 11 | 12 | add_executable(wfb_keygen wifibroadcast/executables/wfb_keygen.cpp) 13 | target_link_libraries(wfb_keygen PRIVATE ${WB_TARGET_LINK_LIBRARIES}) 14 | 15 | add_executable(benchmark wifibroadcast/executables/benchmark.cpp) 16 | target_link_libraries(benchmark PRIVATE ${WB_TARGET_LINK_LIBRARIES}) 17 | 18 | add_executable(udp_generator_validator wifibroadcast/executables/udp_generator_validator.cpp) 19 | target_link_libraries(udp_generator_validator PRIVATE ${WB_TARGET_LINK_LIBRARIES}) 20 | 21 | add_executable(unit_test wifibroadcast/executables/unit_test.cpp) 22 | target_link_libraries(unit_test PRIVATE ${WB_TARGET_LINK_LIBRARIES}) 23 | 24 | add_executable(socket_helper_test wifibroadcast/executables/socket_helper_test.cpp) 25 | target_link_libraries(socket_helper_test PRIVATE ${WB_TARGET_LINK_LIBRARIES}) 26 | 27 | add_executable(udp_packet_drop_util wifibroadcast/executables/udp_packet_drop_util.cpp) 28 | target_link_libraries(udp_packet_drop_util PRIVATE ${WB_TARGET_LINK_LIBRARIES}) 29 | 30 | add_executable(test_txrx wifibroadcast/executables/test_txrx.cpp) 31 | target_link_libraries(test_txrx PRIVATE ${WB_TARGET_LINK_LIBRARIES}) 32 | 33 | add_executable(example_hello wifibroadcast/executables/example_hello.cpp) 34 | target_link_libraries(example_hello PRIVATE ${WB_TARGET_LINK_LIBRARIES}) 35 | 36 | add_executable(example_udp wifibroadcast/executables/example_udp.cpp) 37 | target_link_libraries(example_udp PRIVATE ${WB_TARGET_LINK_LIBRARIES}) 38 | 39 | add_executable(injection_rate_test wifibroadcast/executables/injection_rate_test.cpp) 40 | target_link_libraries(injection_rate_test PRIVATE ${WB_TARGET_LINK_LIBRARIES}) 41 | 42 | add_executable(example_pollute wifibroadcast/executables/example_pollute.cpp) 43 | target_link_libraries(example_pollute PRIVATE ${WB_TARGET_LINK_LIBRARIES}) 44 | 45 | add_executable(test_listen wifibroadcast/executables/test_listen.cpp) 46 | target_link_libraries(test_listen PRIVATE ${WB_TARGET_LINK_LIBRARIES}) 47 | 48 | add_executable(test_dummy_link wifibroadcast/executables/test_dummy_link.cpp) 49 | target_link_libraries(test_dummy_link PRIVATE ${WB_TARGET_LINK_LIBRARIES}) 50 | 51 | add_executable(test_queue wifibroadcast/executables/test_queue.cpp) 52 | target_link_libraries(test_queue PRIVATE ${WB_TARGET_LINK_LIBRARIES}) 53 | 54 | # When it is a static library, we don't need to install it. 55 | # But if it is a shared library, we need to install it. 56 | #install(TARGETS wifibroadcast DESTINATION lib) 57 | install(TARGETS wfb_keygen DESTINATION bin) 58 | install(TARGETS benchmark DESTINATION bin) 59 | install(TARGETS udp_generator_validator DESTINATION bin) 60 | install(TARGETS unit_test DESTINATION bin) 61 | install(TARGETS socket_helper_test DESTINATION bin) 62 | install(TARGETS example_hello DESTINATION bin) 63 | install(TARGETS example_udp DESTINATION bin) 64 | 65 | 66 | -------------------------------------------------------------------------------- /CMakePresets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "configurePresets": [ 4 | { 5 | "name": "linux-debug", 6 | "displayName": "Linux Debug", 7 | "generator": "Ninja", 8 | "binaryDir": "${sourceDir}/out/build/${presetName}", 9 | "installDir": "${sourceDir}/out/install/${presetName}", 10 | "cacheVariables": { 11 | "CMAKE_BUILD_TYPE": "Debug" 12 | }, 13 | "condition": { 14 | "type": "equals", 15 | "lhs": "${hostSystemName}", 16 | "rhs": "Linux" 17 | }, 18 | "vendor": { 19 | "microsoft.com/VisualStudioRemoteSettings/CMake/1.0": { 20 | "sourceDir": "$env{HOME}/.vs/$ms{projectDirName}" 21 | } 22 | } 23 | } 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | c++ Library for wifibroadcast video and telemetry streaming. \ 3 | ## Features 4 | 1) Multiplexing and packet validation / encryption 5 | 2) Zero latency overhead FEC video streaming (requires usage of c++) 6 | 3) SIMD accelerated FEC (NEON on ARM, SSSE3 on x86) 7 | 4) Advanced debugging and statistics (e.g. packet loss) [see](https://github.com/OpenHD/wifibroadcast/blob/master/src/WBTxRx.h#L121) 8 | 5) Simple examples to get started 9 | 6) Basic unit tests for FEC 10 | 11 | # Getting started 12 | ## Compiling 13 | run sudo ./install_dep.sh \ 14 | Compile with cmake (./build_cmake.sh) 15 | 16 | ## Examples 17 | NOTE: You need to first enable monitor mode on your card(s) and the card driver 18 | needs to support active and passive monitor mode (listening & injecting packets) 19 | 1) example_hello: [link](https://github.com/OpenHD/wifibroadcast/blob/master/executables/example_hello.cpp) \ 20 | Bidirectional communication between air and ground unit 21 | 2) benchmark: [link](https://github.com/OpenHD/wifibroadcast/blob/master/executables/benchmark.cpp) \ 22 | Gives a quick overview over FEC and encryption / decryption performance on your platform 23 | 3) example_udp: [link](https://github.com/OpenHD/wifibroadcast/blob/master/executables/example_udp.cpp) \ 24 | Simple unidirectional UDP streaming application, can be used for 25 | rapid development and shows how you could create your own WB link with 26 | multiple unidirectional / bidirectional streams 27 | ### For a more practical application, please check out OpenHD EVO ! 28 | [Project](https://github.com/OpenHD/OpenHD/blob/2.3-evo/OpenHD/ohd_interface/inc/wb_link.h#L31) 29 | 30 | [Link implementation](https://github.com/OpenHD/OpenHD/blob/2.3-evo/OpenHD/ohd_interface/inc/wb_link.h#L31) 31 | 32 | ### Pre unify tx / rx 33 | The design principle of running multiple instances of an application (e.g. wifibroadcast tx / rx) 34 | has a couple of disadvantages. 35 | It makes debugging quite hard (you now have multiple applications for video tx, video rx, and telemetry tx, telemetry rx), 36 | makes threading and sequencing harder and also increases latency on the tx with the udp & rtp approach. 37 | Doing more in c++ and less scripting makes it easy to solve those issues. 38 | However, if you want to use scripting / udp badly, checkout [pre-unify-tx-rx](https://github.com/OpenHD/wifibroadcast/tree/pre-unify-tx-rx) 39 | -------------------------------------------------------------------------------- /ThroughputBenchmarks/fec/Results.txt: -------------------------------------------------------------------------------- 1 | Summary of the test results. Note that these test only measure the FEC encoding performance. 2 | 3 | 4 | | k=20 5 | INTEL | TOTAL Packets per second:101666 before FEC: 1121.59Mbit/s after FEC: 1682.39MBit/s 6 | RPI CM4 (1.5 Ghz) | TOTAL Packets per second:27289.6 before FEC: 301.062Mbit/s after FEC: 451.593MBit/s 7 | RV1126 at 600mhz | TOTAL Packets per second:2696.53 before FEC: 29.7484Mbit/s after FEC: 44.6227MBit/s 8 | RPI CM4 at 600mhz | TOTAL Packets per second:10820.3 before FEC: 119.37Mbit/s after FEC: 179.055MBit/s 9 | RPI 2 at 900mhz | TOTAL Packets per second:4006.13 before FEC_ENCODE: 44.196Mbit/s after FEC_ENCODE: 66.294MBit/s 10 | -------------------------------------------------------------------------------- /ThroughputBenchmarks/fec/cm4_underclocked_with_vs_without_neon.txt: -------------------------------------------------------------------------------- 1 | 2 | XXXXXXXXXXXXXXXXXXXXX - Without neon (old) - XXXXXXXXXXXXXXXXXXXXX 3 | 4 | pi@raspberrypi:~/Desktop/wifibroadcast $ ./benchmark 5 | WARNING cannot set ThreadParamsMaxRealtime 6 | TEST_MAIN has priority 0 7 | TEST_MAIN has policy 0 and priority 0 8 | Benchmark type: 0(FEC_ENCODE) 9 | PacketSize: 1446 B 10 | FEC_K: 10 11 | FEC_PERCENTAGE: 50 12 | Benchmark time: 60 s 13 | GF_SIZE is 255 14 | bad exp/log i 0 log 255 exp(log) 1 15 | test done2 16 | FEC with k max:10 and percentage:50 17 | For a block size of k max this is (10:15) in old (K:N) terms. 18 | curr. Packets per second:24940 before FEC_ENCODE: 275.141Mbit/s after FEC_ENCODE: 412.711MBit/s 19 | ... 20 | curr. Packets per second:24870 before FEC_ENCODE: 274.368Mbit/s after FEC_ENCODE: 411.553MBit/s 21 | Testing FEC_ENCODE took 60.013 seconds 22 | TOTAL Packets per second:24911.9 before FEC_ENCODE: 274.831Mbit/s after FEC_ENCODE: 412.247MBit/s 23 | Performing FEC_BLOCK_ENCODE on 14.121094kB took 0.36251 ms on average 24 | This would equate to a throughput of: 304.326 Mbit/s 25 | 26 | 27 | pi@raspberrypi:~/Desktop/wifibroadcast $ ./benchmark -k 50 -p 20 28 | WARNING cannot set ThreadParamsMaxRealtime 29 | TEST_MAIN has priority 0 30 | TEST_MAIN has policy 0 and priority 0 31 | Benchmark type: 0(FEC_ENCODE) 32 | PacketSize: 1446 B 33 | FEC_K: 50 34 | FEC_PERCENTAGE: 20 35 | Benchmark time: 60 s 36 | GF_SIZE is 255 37 | bad exp/log i 0 log 255 exp(log) 1 38 | test done2 39 | FEC with k max:50 and percentage:20 40 | For a block size of k max this is (50:60) in old (K:N) terms. 41 | curr. Packets per second:12800 before FEC_ENCODE: 141.211Mbit/s after FEC_ENCODE: 169.453MBit/s 42 | ..... 43 | curr. Packets per second:12850 before FEC_ENCODE: 141.763Mbit/s after FEC_ENCODE: 170.115MBit/s 44 | Testing FEC_ENCODE took 60.075 seconds 45 | TOTAL Packets per second:12852.2 before FEC_ENCODE: 141.787Mbit/s after FEC_ENCODE: 170.144MBit/s 46 | Performing FEC_BLOCK_ENCODE on 70.605469kB took 3.69511 ms on average 47 | This would equate to a throughput of: 149.28 Mbit/s 48 | 49 | 50 | 51 | XXXXXXXXXXXXXXXXXXXXX - With NEON enabled (new) - XXXXXXXXXXXXXXXXXXXXX 52 | 53 | pi@raspberrypi:~/Desktop/wifibroadcast $ ./benchmark 54 | WARNING cannot set ThreadParamsMaxRealtime 55 | TEST_MAIN has priority 0 56 | TEST_MAIN has policy 0 and priority 0 57 | Benchmark type: 0(FEC_ENCODE) 58 | PacketSize: 1446 B 59 | FEC_K: 10 60 | FEC_PERCENTAGE: 50 61 | Benchmark time: 60 s 62 | GF_SIZE is 255 63 | bad exp/log i 0 log 255 exp(log) 1 64 | test done2 65 | FEC with k max:10 and percentage:50 66 | For a block size of k max this is (10:15) in old (K:N) terms. 67 | curr. Packets per second:74860 before FEC_ENCODE: 825.863Mbit/s after FEC_ENCODE: 1238.8MBit/s 68 | ... 69 | curr. Packets per second:75082 before FEC_ENCODE: 828.312Mbit/s after FEC_ENCODE: 1242.47MBit/s 70 | Testing FEC_ENCODE took 60.002 seconds 71 | TOTAL Packets per second:74749.5 before FEC_ENCODE: 824.644Mbit/s after FEC_ENCODE: 1236.97MBit/s 72 | Performing FEC_BLOCK_ENCODE on 14.121094kB took 0.0944422 ms on average 73 | This would equate to a throughput of: 1168.13 Mbit/s 74 | 75 | pi@raspberrypi:~/Desktop/wifibroadcast $ ./benchmark -k 50 -p 20 76 | WARNING cannot set ThreadParamsMaxRealtime 77 | TEST_MAIN has priority 0 78 | TEST_MAIN has policy 0 and priority 0 79 | Benchmark type: 0(FEC_ENCODE) 80 | PacketSize: 1446 B 81 | FEC_K: 50 82 | FEC_PERCENTAGE: 20 83 | Benchmark time: 60 s 84 | GF_SIZE is 255 85 | bad exp/log i 0 log 255 exp(log) 1 86 | test done2 87 | FEC with k max:50 and percentage:20 88 | For a block size of k max this is (50:60) in old (K:N) terms. 89 | curr. Packets per second:45550 before FEC_ENCODE: 502.512Mbit/s after FEC_ENCODE: 603.015MBit/s 90 | ... 91 | curr. Packets per second:45552 before FEC_ENCODE: 502.534Mbit/s after FEC_ENCODE: 603.041MBit/s 92 | Testing FEC_ENCODE took 60.016 seconds 93 | TOTAL Packets per second:45607 before FEC_ENCODE: 503.142Mbit/s after FEC_ENCODE: 603.77MBit/s 94 | Performing FEC_BLOCK_ENCODE on 70.605469kB took 0.903329 ms on average 95 | This would equate to a throughput of: 610.636 Mbit/s 96 | 97 | 98 | SUMMARRY: 99 | -k 10 -p 50: (10:15) in old (K:N) terms. 100 | TOTAL Packets per second:24911.9 before FEC_ENCODE: 274.831Mbit/s after FEC_ENCODE: 412.247MBit/s 101 | Performing FEC_BLOCK_ENCODE on 14.121094kB took 0.36251 ms on average 102 | vs 103 | TOTAL Packets per second:74749.5 before FEC_ENCODE: 824.644Mbit/s after FEC_ENCODE: 1236.97MBit/s 104 | Performing FEC_BLOCK_ENCODE on 14.121094kB took 0.0944422 ms on average 105 | 106 | -k 50 -p 20 (50:60) in old (K:N) terms. 107 | TOTAL Packets per second:12852.2 before FEC_ENCODE: 141.787Mbit/s after FEC_ENCODE: 170.144MBit/s 108 | Performing FEC_BLOCK_ENCODE on 70.605469kB took 3.69511 ms on average 109 | vs 110 | TOTAL Packets per second:45607 before FEC_ENCODE: 503.142Mbit/s after FEC_ENCODE: 603.77MBit/s 111 | Performing FEC_BLOCK_ENCODE on 70.605469kB took 0.903329 ms on average -------------------------------------------------------------------------------- /ThroughputBenchmarks/fec/other.txt: -------------------------------------------------------------------------------- 1 | rpi cm4 @ 600Mhz 2 | also measuring the FEC step duration: 3 | 4 | pi@raspberrypi:~/Desktop/wifibroadcast $ ./benchmark 5 | WARNING cannot set ThreadParamsMaxRealtime 6 | TEST_MAIN has priority 0 7 | TEST_MAIN has policy 0 and priority 0 8 | Benchmark type: 0(FEC_ENCODE) 9 | PacketSize: 1446 B 10 | FEC_K: 10 11 | FEC_PERCENTAGE: 50 12 | Benchmark time: 60 s 13 | FEC with k max:10 and percentage:50 14 | For a block size of k max this is (10:15) in old (K:N) terms. 15 | ... 16 | Testing FEC_ENCODE took 60.022 seconds 17 | TOTAL Packets per second:20779.6 before FEC_ENCODE: 229.243Mbit/s after FEC_ENCODE: 343.864MBit/s 18 | Encoding a block of size:14.121094kB took 0.442483 ms on average 19 | 20 | 21 | pi@raspberrypi:~/Desktop/wifibroadcast $ ./benchmark -k 20 22 | WARNING cannot set ThreadParamsMaxRealtime 23 | TEST_MAIN has priority 0 24 | TEST_MAIN has policy 0 and priority 0 25 | Benchmark type: 0(FEC_ENCODE) 26 | PacketSize: 1446 B 27 | FEC_K: 20 28 | FEC_PERCENTAGE: 50 29 | Benchmark time: 60 s 30 | FEC with k max:20 and percentage:50 31 | For a block size of k max this is (20:30) in old (K:N) terms. 32 | curr. Packets per second:10660 before FEC_ENCODE: 117.602Mbit/s after FEC_ENCODE: 176.403MBit/s 33 | ... 34 | curr. Packets per second:10740 before FEC_ENCODE: 118.485Mbit/s after FEC_ENCODE: 177.727MBit/s 35 | Testing FEC_ENCODE took 60.003 seconds 36 | TOTAL Packets per second:10717.3 before FEC_ENCODE: 118.235Mbit/s after FEC_ENCODE: 177.352MBit/s 37 | Encoding a block of size:28.242188kB took 1.78779 ms on average 38 | 39 | pi@raspberrypi:~/Desktop/wifibroadcast $ ./benchmark -k 50 40 | WARNING cannot set ThreadParamsMaxRealtime 41 | TEST_MAIN has priority 0 42 | TEST_MAIN has policy 0 and priority 0 43 | Benchmark type: 0(FEC_ENCODE) 44 | PacketSize: 1446 B 45 | FEC_K: 50 46 | FEC_PERCENTAGE: 50 47 | Benchmark time: 60 s 48 | FEC with k max:50 and percentage:50 49 | For a block size of k max this is (50:75) in old (K:N) terms. 50 | curr. Packets per second:4350 before FEC_ENCODE: 47.9897Mbit/s after FEC_ENCODE: 71.9845MBit/s 51 | ... 52 | curr. Packets per second:4350 before FEC_ENCODE: 47.9897Mbit/s after FEC_ENCODE: 71.9845MBit/s 53 | Testing FEC_ENCODE took 60.213 seconds 54 | TOTAL Packets per second:4319.6 before FEC_ENCODE: 47.6543Mbit/s after FEC_ENCODE: 71.4814MBit/s 55 | Encoding a block of size:70.605469kB took 11.3778 ms on average 56 | 57 | --------------------------------------------------------------------------------------------------- 58 | 59 | rv1126 @600Mhz 60 | 61 | [root@RV1126_RV1109:/oem/usr/bin]# ./benchmark 62 | TEST_MAIN has priority 0 63 | TEST_MAIN has policy 1 and priority 99 64 | Benchmark type: 0(FEC_ENCODE) 65 | PacketSize: 1446 B 66 | FEC_K: 10 67 | FEC_PERCENTAGE: 50 68 | Benchmark time: 60 s 69 | FEC with k max:10 and percentage:50 70 | For a block size of k max this is (10:15) in old (K:N) terms. 71 | curr. Packets per second:5400 before FEC_ENCODE: 59.5734Mbit/s after FEC_ENCODE: 89.36MBit/s 72 | curr. Packets per second:5400 before FEC_ENCODE: 59.5734Mbit/s after FEC_ENCODE: 89.36MBit/s 73 | Testing FEC_ENCODE took 60.004 seconds 74 | TOTAL Packets per second:5426.84 before FEC_ENCODE: 59.8694Mbit/s after FEC_ENCODE: 89.8042MBit/s 75 | Encoding a block of size:14.121094kB took 1.81136 ms on average 76 | 77 | -------------------------------------------------------------------------------- /build_cmake.sh: -------------------------------------------------------------------------------- 1 | # bin/bash 2 | 3 | # convenient script to build this project with cmake 4 | 5 | rm -rf build 6 | 7 | mkdir build 8 | 9 | cd build 10 | 11 | cmake .. 12 | 13 | make -j$(nproc) -------------------------------------------------------------------------------- /example_key/README.md: -------------------------------------------------------------------------------- 1 | Example tx / rx key, generated from the default openhd bind phrase "openhd" 2 | -------------------------------------------------------------------------------- /example_key/generate.sh: -------------------------------------------------------------------------------- 1 | #bin/bash 2 | 3 | ./../cmake-build-debug/wfb_keygen -b openhd 4 | -------------------------------------------------------------------------------- /example_key/txrx.key: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenHD/wifibroadcast/99e53e7ea6dfafff85999504481d7cf26e3ee749/example_key/txrx.key -------------------------------------------------------------------------------- /install_dep.sh: -------------------------------------------------------------------------------- 1 | # bin/bash 2 | 3 | apt -y install libpcap-dev libsodium-dev || exit 1 4 | apt -y install libspdlog-dev || exit 1 -------------------------------------------------------------------------------- /latencyTesting/Readme.md: -------------------------------------------------------------------------------- 1 | Here you can find the sources to build a simple "testing utility" 2 | that tracks packet loss and latency.\ 3 | It is not configured to run on OpenHD at the time but rather use a ubuntu pc with two (or more) wifi cards connected 4 | & set into monitor mode. See the .sh scripts to loopUntilError wb from your pc. 5 | 6 | If you do testing for a specific card or something similar, this is the place to store & share your results. 7 | 8 | 9 | -------------------------------------------------------------------------------- /latencyTesting/SimpleTestProgram/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.a 3 | *.pyc 4 | dist/ 5 | deb_dist/ 6 | build/ 7 | env/ 8 | _trial_temp/ 9 | test 10 | gs.key 11 | key_1.key 12 | wfb_keygen 13 | telemetry/conf/site.cfg 14 | telemetry/conf/local.cfg 15 | 16 | ### JetBrains template 17 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 18 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 19 | .idea 20 | 21 | # User-specific stuff: 22 | .idea/workspace.xml 23 | .idea/tasks.xml 24 | .idea/dictionaries 25 | .idea/vcs.xml 26 | .idea/jsLibraryMappings.xml 27 | 28 | # Sensitive or high-churn files: 29 | .idea/dataSources.ids 30 | .idea/dataSources.xml 31 | .idea/dataSources.local.xml 32 | .idea/sqlDataSources.xml 33 | .idea/dynamic.xml 34 | .idea/uiDesigner.xml 35 | 36 | # Gradle: 37 | .idea/gradle.xml 38 | .idea/libraries 39 | 40 | # Mongo Explorer plugin: 41 | .idea/mongoSettings.xml 42 | 43 | ## File-based project format: 44 | *.iws 45 | 46 | ## Plugin-specific files: 47 | 48 | # IntelliJ 49 | /out/ 50 | 51 | # mpeltonen/sbt-idea plugin 52 | .idea_modules/ 53 | 54 | # JIRA plugin 55 | atlassian-ide-plugin.xml 56 | 57 | # Crashlytics plugin (for Android Studio and IntelliJ) 58 | com_crashlytics_export_strings.xml 59 | crashlytics.properties 60 | crashlytics-build.properties 61 | fabric.properties 62 | 63 | misc.xml 64 | .idea/* 65 | 66 | -------------------------------------------------------------------------------- /latencyTesting/SimpleTestProgram/Makefile: -------------------------------------------------------------------------------- 1 | 2 | # bring in the helper from the parent / wifibroadcast / HelperSources File 3 | # kinda dirty (fixme but how) 4 | HELPER_DIR := ../../src/HelperSources/ 5 | 6 | all: test udp_forwarder 7 | 8 | test : test.cpp 9 | g++ -std=c++17 test.cpp UDPReceiver.cpp UDPSender.cpp -o test -lpthread -I $(HELPER_DIR) 10 | 11 | udp_forwarder : udp_forwarder.cpp 12 | g++ -std=c++17 udp_forwarder.cpp UDPReceiver.cpp UDPSender.cpp -o udp_forwarder -lpthread -I $(HELPER_DIR) 13 | 14 | clean: 15 | rm -rf test 16 | rm -rf udp_forwarder -------------------------------------------------------------------------------- /latencyTesting/SimpleTestProgram/README.md: -------------------------------------------------------------------------------- 1 | Compile code: 2 | Just run make 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /latencyTesting/SimpleTestProgram/UDPReceiver.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef FPV_VR_UDPRECEIVER_H 3 | #define FPV_VR_UDPRECEIVER_H 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "TimeHelper.hpp" 15 | #include 16 | // 17 | 18 | //Starts a new thread that continuously checks for new data on UDP port 19 | class UDPReceiver { 20 | public: 21 | typedef std::function DATA_CALLBACK; 22 | public: 23 | /** 24 | * @param port : The port to listen on 25 | * @param onDataReceivedCallback: called every time new data is received 26 | * @param WANTED_RCVBUF_SIZE: The buffer allocated by the OS might not be sufficient to buffer incoming data when receiving at a high data rate 27 | * If @param WANTED_RCVBUF_SIZE is bigger than the size allocated by the OS a bigger buffer is requested, but it is not 28 | * guaranteed that the size is actually increased. Use 0 to leave the buffer size untouched 29 | */ 30 | UDPReceiver(int port,std::string name,DATA_CALLBACK onDataReceivedCallback, 31 | size_t WANTED_RCVBUF_SIZE=0,const bool ENABLE_NONBLOCKING=false); 32 | /** 33 | * Start receiver thread,which opens UDP port 34 | */ 35 | void startReceiving(); 36 | /** 37 | * Stop and join receiver thread, which closes port 38 | */ 39 | void stopReceiving(); 40 | //Get function(s) for private member variables 41 | long getNReceivedBytes()const; 42 | std::string getSourceIPAddress()const; 43 | int getPort()const; 44 | private: 45 | void receiveFromUDPLoop(); 46 | const DATA_CALLBACK onDataReceivedCallback=nullptr; 47 | const int mPort; 48 | const size_t WANTED_RCVBUF_SIZE; 49 | const std::string mName; 50 | ///We need this reference to stop the receiving thread 51 | int mSocket=0; 52 | std::string senderIP="0.0.0.0"; 53 | std::atomic receiving=false; 54 | std::atomic nReceivedBytes=0; 55 | std::unique_ptr mUDPReceiverThread; 56 | //https://en.wikipedia.org/wiki/User_Datagram_Protocol 57 | //65,507 bytes (65,535 − 8 byte UDP header − 20 byte IP header). 58 | static constexpr const size_t UDP_PACKET_MAX_SIZE=65507; 59 | std::chrono::steady_clock::time_point lastReceivedPacket{}; 60 | AvgCalculator avgDeltaBetweenPackets; 61 | const bool ENABLE_NONBLOCKING; 62 | }; 63 | 64 | #endif // FPV_VR_UDPRECEIVER_H -------------------------------------------------------------------------------- /latencyTesting/SimpleTestProgram/UDPSender.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by geier on 28/02/2020. 3 | // 4 | 5 | #include "UDPSender.h" 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "StringHelper.hpp" 15 | #include 16 | 17 | UDPSender::UDPSender(const std::string &IP,const int Port,const int WANTED_SNDBUFF_SIZE): 18 | WANTED_SNDBUFF_SIZE(WANTED_SNDBUFF_SIZE) 19 | { 20 | //create the socket 21 | sockfd = socket(AF_INET,SOCK_DGRAM,0); 22 | if (sockfd < 0) { 23 | std::cout<<"Cannot create socket\n"; 24 | } 25 | //Create the address 26 | address.sin_family = AF_INET; 27 | address.sin_port = htons(Port); 28 | inet_pton(AF_INET,IP.c_str(), &address.sin_addr); 29 | // 30 | int sendBufferSize=0; 31 | socklen_t len=sizeof(sendBufferSize); 32 | getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &sendBufferSize, &len); 33 | std::cout<<"Default socket send buffer is "<UDP_PACKET_MAX_SIZE){ 46 | std::cerr<<"Data size exceeds UDP packet size"; 47 | return; 48 | } 49 | nSentBytes+=data_length; 50 | // Measure the time this call takes (is there some funkiness ? ) 51 | timeSpentSending.start(); 52 | const auto result= sendto(sockfd, data, data_length, 0, (struct sockaddr *) &(address), 53 | sizeof(struct sockaddr_in)); 54 | if(result<0){ 55 | std::cerr<<"Cannot send data "<100){ 61 | //std::cout<<"TimeSS "< 9 | #include 10 | #include 11 | #include "TimeHelper.hpp" 12 | 13 | /** 14 | * Allows sending UDP data on the current thread. No extra thread for sending is created (make sure to not call mySendTo() on the UI thread) 15 | */ 16 | class UDPSender{ 17 | public: 18 | /** 19 | * Construct a UDP sender that sends UDP data packets 20 | * @param IP ipv4 address to send data to 21 | * @param Port port for sending data 22 | * @param WANTED_SNDBUFF_SIZE: If not set to 0, increase the buffer allocated by the OS to buffer data before sending UDP packets out 23 | * Should not increase latency (data is not only sent when this buffer is full) 24 | */ 25 | UDPSender(const std::string& IP,const int Port,const int WANTED_SNDBUFF_SIZE=0); 26 | ~UDPSender(); 27 | // Send one udp packet. Packet size must not exceed the max UDP packet size 28 | // Do not rename to sendto() because this method also exists from the linux socket lib 29 | // (This method does nothing else than validate the data size, then call sendto() 30 | void mySendTo(const uint8_t* data, ssize_t data_length); 31 | //https://en.wikipedia.org/wiki/User_Datagram_Protocol 32 | //65,507 bytes (65,535 − 8 byte UDP header − 20 byte IP header). 33 | static constexpr const size_t UDP_PACKET_MAX_SIZE=65507; 34 | std::size_t nSentBytes=0; 35 | static constexpr std::size_t EXAMPLE_MEDIUM_SNDBUFF_SIZE=1024*1024; 36 | void logSendtoDelay(); 37 | private: 38 | int sockfd; 39 | sockaddr_in address{}; 40 | Chronometer timeSpentSending; 41 | const int WANTED_SNDBUFF_SIZE; 42 | }; 43 | 44 | 45 | #endif //LIVEVIDEOSTREAMPRODUCER_UDPSENDER_H 46 | -------------------------------------------------------------------------------- /latencyTesting/SimpleTestProgram/udp_forwarder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenHD/wifibroadcast/99e53e7ea6dfafff85999504481d7cf26e3ee749/latencyTesting/SimpleTestProgram/udp_forwarder -------------------------------------------------------------------------------- /latencyTesting/SimpleTestProgram/udp_forwarder.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by consti10 on 15.10.21. 3 | // 4 | #include "TimeHelper.hpp" 5 | #include "Helper.hpp" 6 | #include "UDPSender.h" 7 | #include "UDPReceiver.h" 8 | #include "StringHelper.hpp" 9 | #include "SchedulingHelper.hpp" 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | // Forward udp data from localhost(ip):port to destination(ip):port 19 | 20 | static bool quit = false; 21 | static void sigterm_handler(int sig) { 22 | fprintf(stderr, "signal %d\n", sig); 23 | quit = true; 24 | } 25 | 26 | static std::unique_ptr udpSender= nullptr; 27 | 28 | static void forwardData(const uint8_t* dataP,size_t data_length){ 29 | udpSender->mySendTo(dataP,data_length); 30 | std::cout<<"Forwarded data "<(outputIp,outputPort); 65 | 66 | UDPReceiver udpReceiver{inputPort,"ForwardRec", forwardData,0,false}; 67 | udpReceiver.startReceiving(); 68 | 69 | std::cout<<"Now forwarding from localhost:"< Results, run Nr 1 12 | WANTED_PACKETS_PER_SECOND 3000 Got 3000.05 13 | BITRATE: 4.13711MB/s (33.0969MBit/s) 14 | N of packets sent | rec | diff [60000 | 59890 | 110] 15 | nReceivedPacketsWhereContentDoesntMatch 0 16 | LostPacketsSeqNrDiffs 3,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,2,2,2,2,2,2,2,2,3,2,2,3,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 17 | ------- Latency between (I<=>O) ------- 18 | min=814.234009us max=31.474001ms avg=23.489000ms N samples=59890 19 | 20 | => Results, run Nr 2: 21 | WANTED_PACKETS_PER_SECOND 3000 Got 3000.05 22 | BITRATE: 4.13711MB/s (33.0969MBit/s) 23 | N of packets sent | rec | diff [60000 | 59899 | 101] 24 | nReceivedPacketsWhereContentDoesntMatch 0 25 | LostPacketsSeqNrDiffs 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 26 | ------- Latency between (I<=>O) ------- 27 | min=573.908997us max=31.489000ms avg=22.995001ms N samples=59899 28 | 29 | 30 | ---------------------------------------------------------------------------- 31 | simple test program: 32 | sudo ./test -i 6100 -o 6000 -s 1446 -p 2000 -t 20 33 | 34 | => Results, run Nr 1 35 | WANTED_PACKETS_PER_SECOND 2000 Got 2000.05 36 | BITRATE: 2.75809MB/s (22.0648MBit/s) 37 | N of packets sent | rec | diff [40000 | 39999 | 1] 38 | nReceivedPacketsWhereContentDoesntMatch 0 39 | LostPacketsSeqNrDiffs 2, 40 | ------- Latency between (I<=>O) ------- 41 | min=488.497986us max=7.906000ms avg=1.286000ms N samples=39999 42 | 43 | => Results, run Nr 2: 44 | ANTED_PACKETS_PER_SECOND 2000 Got 2000.05 45 | BITRATE: 2.75809MB/s (22.0647MBit/s) 46 | N of packets sent | rec | diff [40000 | 39997 | 3] 47 | nReceivedPacketsWhereContentDoesntMatch 0 48 | LostPacketsSeqNrDiffs 2,2,2, 49 | ------- Latency between (I<=>O) ------- 50 | min=504.160004us max=5.055000ms avg=1.270000ms N samples=39997 51 | 52 | Conclusion from test environment 1: 53 | 22MBit/s seems to be the "sweet spot" here. 33MBit/s also "works", but latency increases 54 | 55 | 56 | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 57 | Now same as above, but done on rpi cm4 usb ports: 58 | simple test program: 59 | sudo ./test -i 6100 -o 6000 -s 1446 -p 2000 -t 20 60 | 61 | since 33Mbit/s gave "almost issues" on ubuntu, here I am starting with the slower one: 62 | => Results,run Nr 1: 63 | ANTED_PACKETS_PER_SECOND 2000 Got 2000.05 64 | BITRATE: 2.75809MB/s (22.0647MBit/s) 65 | N of packets sent | rec | diff [40000 | 2642 | 37358] 66 | nReceivedPacketsWhereContentDoesntMatch 0 67 | LostPacketsSeqNrDiffs 3,4,2,2,3,2,2,2,3,2,21,23,2,2,65,2,83,2,83,2,2,99,2,102,122,2,170,2,160,2,160,2,160,2,162,2,2,161, 68 | ------- Latency between (I<=>O) ------- 69 | min=788.083984us max=5.232000ms avg=1.627000ms N samples=2642 70 | Note: Worked fine in the beginning, then stopped working 71 | (that's where all the lost packets "in the end" come from) 72 | 73 | => Results,run Nr 2: 74 | ANTED_PACKETS_PER_SECOND 2000 Got 2000.05 75 | BITRATE: 2.75809MB/s (22.0647MBit/s) 76 | N of packets sent | rec | diff [40000 | 0 | 40000] 77 | nReceivedPacketsWhereContentDoesntMatch 0 78 | LostPacketsSeqNrDiffs 79 | ------- Latency between (I<=>O) ------- 80 | min=0ns max=0ns avg=0ns N samples=0 81 | 82 | Once broken, it doesn't work again, as you can see here. 83 | 84 | Plugging it out and back in, restarting tx and rx: 85 | => Results,run Nr 3: 86 | ANTED_PACKETS_PER_SECOND 2000 Got 2000.05 87 | BITRATE: 2.75809MB/s (22.0647MBit/s) 88 | N of packets sent | rec | diff [40000 | 7148 | 32852] 89 | nReceivedPacketsWhereContentDoesntMatch 0 90 | LostPacketsSeqNrDiffs 6,24,51,37,3,12,2,4,8,10,17,2,26, ... (didn't copy since it goes on for ages) 91 | 92 | 93 | => Results,run Nr 4: 94 | Okay, doesn't work again 95 | 96 | 97 | -------------------------------------------------------------------------------- /latencyTesting/TestResults/test1/startTxAndRxOnMyPc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Given a PC with 2 wifi cards connected that support monitor mode, 3 | # This starts the tx on one of them and the rx on the other one 4 | 5 | TAOBAO="wlx00e0863200b9" #Taobao card 6 | ASUS="wlx244bfeb71c05" #ASUS card 7 | 8 | #MY_TX="wlp3s0" 9 | #MY_TX="wlxc4e984126183" 10 | #MY_TX="wlx000f00460445" # ALFA card 11 | #MY_TX="wlx6cfdb9b2a156" #PW-DN421 12 | #MY_TX="wlx0018e7bd24db" 13 | #MY_RX="wlx0018e7bd24db" #Ralink card aliexpress 14 | #MY_RX="wlxc4e9840e3cbe" #tp-link rx 15 | #MY_RX_SECONDARY="wlxc4e984126183" 16 | 17 | #MY_TX=$ASUS 18 | #MY_RX=$TAOBAO 19 | MY_TX="wlan0" #rpi testing, alfa 20 | MY_RX="wlan1" #rpi testing, taobao 21 | 22 | #WFB_FOLDER="/home/consti10/Desktop/wifibroadcast" 23 | WFB_FOLDER="/home/pi/Desktop/wifibroadcast" 24 | 25 | FEC_K=1 26 | FEC_N=1 27 | 28 | MY_WIFI_CHANNEL=149 #5ghz channel 29 | #MY_WIFI_CHANNEL=13 #2.4ghz channel 30 | 31 | sudo rfkill unblock wifi 32 | #sudo killall ifplugd #stop management of interface 33 | 34 | sudo ifconfig $MY_TX down 35 | sudo iw dev $MY_TX set monitor otherbss fcsfail 36 | sudo ifconfig $MY_TX up 37 | sudo iwconfig $MY_TX channel $MY_WIFI_CHANNEL 38 | #sudo iw dev $MY_TX set channel "6" HT40+ 39 | #sudo iwconfig $MY_TX rts off 40 | 41 | sudo ifconfig $MY_RX down 42 | sudo iw dev $MY_RX set monitor otherbss fcsfail 43 | sudo ifconfig $MY_RX up 44 | sudo iwconfig $MY_RX channel $MY_WIFI_CHANNEL 45 | #sudo iwconfig $MY_RX channel "6" HT40+ 46 | 47 | ## test with multiple RXes 48 | #sudo ifconfig $MY_RX_SECONDARY down 49 | #sudo iw dev $MY_RX_SECONDARY set monitor otherbss fcsfail 50 | #sudo ifconfig $MY_RX_SECONDARY up 51 | #sudo iwconfig $MY_RX_SECONDARY channel 6 52 | 53 | 54 | # $WFB_FOLDER/wfb_tx -k $FEC_K -n $FEC_N -u 6000 -p 60 -M 7 -K $WFB_FOLDER/drone.key $MY_TX 55 | xterm -hold -e $WFB_FOLDER/wfb_tx -k $FEC_K -n $FEC_N -u 6000 -p 60 -M 5 -B 20 -K $WFB_FOLDER/drone.key $MY_TX & 56 | 57 | $WFB_FOLDER/wfb_rx -c 127.0.0.1 -u 6100 -p 60 -K $WFB_FOLDER/gs.key $MY_RX 58 | 59 | #other usefull commands: 60 | #sudo iw dev 61 | #nc -u localhost 6000 62 | #nc -u -l localhost 6100 63 | -------------------------------------------------------------------------------- /latencyTesting/TestResults/test2/results.txt: -------------------------------------------------------------------------------- 1 | Aparently I was able to "catch a window" where RF noise is low. Here are a couple of runs, 2 | these are "so far" the best results I got. 3 | 4 | BW=20, MCS=7 5 | 6 | BITRATE: 4.13711MB/s (33.0969MBit/s) 7 | N of packets sent | rec | diff [60000 | 59998 | 2] 8 | nReceivedPacketsWhereContentDoesntMatch 0 9 | LostPacketsSeqNrDiffs 2, 10 | ------- Latency between (I<=>O) ------- 11 | min=528.327026us max=5.015000ms avg=1.436000ms N samples=59998 12 | 13 | 33MBit/s with pretty much zero packet loss and no packet delays. 14 | So it is possible to get 33MBit/s with low latency if the rf interference is low. 15 | I strongly think that rf interference is noticable in 2 ways: 16 | a) packet loss 17 | b) increased latency, probably created at the TX queuing packets 18 | 19 | 20 | 21 | BITRATE: 4.68872MB/s (37.5098MBit/s) 22 | N of packets sent | rec | diff [68000 | 67999 | 1] 23 | nReceivedPacketsWhereContentDoesntMatch 0 24 | LostPacketsSeqNrDiffs 25 | ------- Latency between (I<=>O) ------- 26 | min=578.356995us max=5.108000ms avg=2.190000ms N samples=67999 27 | 28 | Anything higher than 37 MBit/s and the latency increases greatly -------------------------------------------------------------------------------- /latencyTesting/TestResults/test_wfb_tl_vs_alfa.txt: -------------------------------------------------------------------------------- 1 | TL-WN722N V1 as RX 2 | ./test -m 1 -p 1024 3 | 4 | A) TX is also TL-WN722N V1 5 | Avg UDP latency between (I<=>O)min=14.433000ms max=223.886002ms avg=177.022003ms N samples=2501 6 | LostPacketsSeqNrDiffs 21323141716181317141913181617122315171623141718181318161613221518141912171419131719181221131713231517151813171919122014181321141713231417181913191418131914181421241716191518141912211616132113171618161618181220401715191220171813171517122414161618131718191222131812201517162116161518121720181222121813181416162414171419132014191322141613 7 | Low&high 8 | One Percent low: 9 | ,14.433000ms,15.383000ms,16.176001ms,17.059000ms,18.013000ms,18.756001ms,19.921000ms,20.818001ms,21.551001ms,21.622999ms 10 | ,24.709000ms,25.628000ms,26.474001ms,27.391001ms,28.253000ms,29.172001ms,30.018999ms,30.937000ms,31.851000ms,32.688000ms 11 | ,34.082001ms,34.999001ms,35.917000ms,36.743000ms,37.666000ms 12 | One Percent high: 13 | ,211.412994ms,211.809006ms,211.985001ms,212.050995ms,212.093994ms,212.552002ms,212.692001ms,212.964005ms,213.227005ms,213.315994ms 14 | ,213.783997ms,213.869995ms,214.182999ms,214.589996ms,214.942001ms,215.322998ms,215.753006ms,215.968994ms,216.330002ms,220.830002ms 15 | ,221.636002ms,222.386002ms,223.315994ms,223.438995ms,223.886002ms 16 | 17 | 18 | b) TX is ALFA AWUS036NHA 19 | Avg UDP latency between (I<=>O)min=3.470000ms max=62.939999ms avg=18.461000ms N samples=5116 20 | LostPacketsSeqNrDiffs 5 21 | Low&high 22 | One Percent low: 23 | ,3.470000ms,3.813000ms,3.912000ms,4.294000ms,4.429000ms,4.609000ms,4.662000ms,4.683000ms,4.699000ms,4.802000ms 24 | ,4.961000ms,5.092000ms,5.213000ms,5.260000ms,5.310000ms,5.328000ms,5.338000ms,5.339000ms,5.340000ms,5.376000ms 25 | ,5.376000ms,5.429000ms,5.437000ms,5.489000ms,5.505000ms,5.537000ms,5.554000ms,5.574000ms,5.577000ms,5.588000ms 26 | ,5.596000ms,5.603000ms,5.609000ms,5.615000ms,5.625000ms,5.638000ms,5.639000ms,5.698000ms,5.714000ms,5.749000ms 27 | ,5.763000ms,5.780000ms,5.782000ms,5.784000ms,5.795000ms,5.803000ms,5.808000ms,5.823000ms,5.852000ms,5.871000ms 28 | ,5.871000ms 29 | One Percent high: 30 | ,46.215000ms,46.370998ms,46.490002ms,46.553001ms,46.556999ms,46.577000ms,46.770000ms,46.778999ms,46.855999ms,47.282001ms 31 | ,47.298000ms,47.487000ms,47.747002ms,48.088001ms,48.187000ms,48.196999ms,48.714001ms,48.894001ms,48.928001ms,48.967999ms 32 | ,49.563999ms,49.576000ms,49.630001ms,49.638000ms,50.209999ms,50.356998ms,50.467999ms,50.500000ms,50.978001ms,51.053001ms 33 | ,51.098000ms,51.689999ms,51.896999ms,52.223999ms,52.812000ms,53.164001ms,53.584999ms,53.826000ms,54.436001ms,54.695000ms 34 | ,55.202000ms,55.574001ms,56.127998ms,56.780998ms,57.639999ms,58.675999ms,59.306000ms,60.231998ms,61.117001ms,62.063999ms 35 | ,62.939999ms 36 | 37 | -------------------------------------------------------------------------------- /latencyTesting/XstartTxSimple.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Given a PC with 2 wifi cards connected that support monitor mode, 3 | # This starts the tx on one of them and the rx on the other one 4 | 5 | TAOBAO="wlx00e0863200b9" #Taobao card 6 | ASUS="wlx244bfeb71c05" #ASUS card 7 | #ASUS2="wlxac9e175ac9e8" #ASUS card 2 8 | ASUS2="wlxac9e17596103" 9 | 10 | #MY_TX="wlp3s0" 11 | #MY_TX="wlxc4e984126183" 12 | #MY_TX="wlx000f00460445" # ALFA card 13 | #MY_TX="wlx6cfdb9b2a156" #PW-DN421 14 | #MY_TX="wlx0018e7bd24db" 15 | #MY_RX="wlx0018e7bd24db" #Ralink card aliexpress 16 | #MY_RX="wlxc4e9840e3cbe" #tp-link rx 17 | #MY_RX_SECONDARY="wlxc4e984126183" 18 | 19 | #MY_TX=$ASUS 20 | #MY_RX=$ASUS2 21 | MY_TX="wlan0" #rpi testing 22 | MY_RX="wlan2" #rpi testing 23 | 24 | #WFB_FOLDER="/home/consti10/Desktop/wifibroadcast/build" 25 | WFB_FOLDER="/home/openhd/wifibroadcast/build" 26 | #WFB_FOLDER="/home/pi/Desktop/wifibroadcast" 27 | 28 | FEC_K=0 29 | FEC_PERCENTAGE=20 30 | 31 | RADIO_PORT=60 32 | 33 | #MY_WIFI_CHANNEL=149 #5ghz channel 34 | MY_WIFI_CHANNEL=1 #2.4ghz channel 35 | 36 | sudo rfkill unblock wifi 37 | #sudo killall ifplugd #stop management of interface 38 | 39 | sudo ifconfig $MY_TX down 40 | sudo iw dev $MY_TX set monitor otherbss fcsfail 41 | sudo ifconfig $MY_TX up 42 | sudo iwconfig $MY_TX channel $MY_WIFI_CHANNEL 43 | #sudo iw dev $MY_TX set channel "6" HT40+ 44 | #sudo iwconfig $MY_TX rts off 45 | 46 | #sudo ifconfig $MY_RX down 47 | #sudo iw dev $MY_RX set monitor otherbss fcsfail 48 | #sudo ifconfig $MY_RX up 49 | #sudo iwconfig $MY_RX channel $MY_WIFI_CHANNEL 50 | #sudo iwconfig $MY_RX channel "6" HT40+ 51 | 52 | ## test with multiple RXes 53 | #sudo ifconfig $MY_RX_SECONDARY down 54 | #sudo iw dev $MY_RX_SECONDARY set monitor otherbss fcsfail 55 | #sudo ifconfig $MY_RX_SECONDARY up 56 | #sudo iwconfig $MY_RX_SECONDARY channel 6 57 | 58 | 59 | # $WFB_FOLDER/wfb_tx -k $FEC_K -n $FEC_N -u 6000 -p 60 -M 7 -K $WFB_FOLDER/drone.key $MY_TX 60 | $WFB_FOLDER/wfb_tx -k $FEC_K -p $FEC_PERCENTAGE -u 5600 -r $RADIO_PORT -M 6 -B 20 $MY_TX 61 | 62 | #$WFB_FOLDER/wfb_rx -c 127.0.0.1 -u 5601 -r $RADIO_PORT -K $WFB_FOLDER/gs.key $MY_RX 63 | 64 | #other usefull commands: 65 | #sudo iw dev 66 | #nc -u localhost 6000 67 | #nc -u -l localhost 6100 -------------------------------------------------------------------------------- /latencyTesting/YstartRxSimple.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Given a PC with 2 wifi cards connected that support monitor mode, 3 | # This starts the tx on one of them and the rx on the other one 4 | 5 | TAOBAO="wlx00e0863200b9" #Taobao card 6 | ASUS="wlx244bfeb71c05" #ASUS card 7 | #ASUS2="wlxac9e175ac9e8" #ASUS card 2 8 | ASUS2="wlxac9e17596103" 9 | 10 | #MY_TX="wlp3s0" 11 | #MY_TX="wlxc4e984126183" 12 | #MY_TX="wlx000f00460445" # ALFA card 13 | #MY_TX="wlx6cfdb9b2a156" #PW-DN421 14 | #MY_TX="wlx0018e7bd24db" 15 | #MY_RX="wlx0018e7bd24db" #Ralink card aliexpress 16 | #MY_RX="wlxc4e9840e3cbe" #tp-link rx 17 | #MY_RX_SECONDARY="wlxc4e984126183" 18 | 19 | #MY_TX=$ASUS 20 | #MY_RX=$ASUS 21 | #MY_TX="wlan0" #rpi testing 22 | #MY_RX="wlan1" #rpi testing 23 | MY_RX="wlx6cfdb9b2a150" 24 | 25 | WFB_FOLDER="/home/consti10/Desktop/wifibroadcast/build" 26 | #WFB_FOLDER="/home/pi/Desktop/wifibroadcast" 27 | 28 | FEC_K=0 29 | FEC_PERCENTAGE=0 30 | 31 | RADIO_PORT=60 32 | 33 | #MY_WIFI_CHANNEL=149 #5ghz channel 34 | MY_WIFI_CHANNEL=1 #2.4ghz channel 35 | 36 | sudo rfkill unblock wifi 37 | #sudo killall ifplugd #stop management of interface 38 | 39 | #sudo ifconfig $MY_TX down 40 | #sudo iw dev $MY_TX set monitor otherbss fcsfail 41 | #sudo ifconfig $MY_TX up 42 | #sudo iwconfig $MY_TX channel $MY_WIFI_CHANNEL 43 | #sudo iw dev $MY_TX set channel "6" HT40+ 44 | #sudo iwconfig $MY_TX rts off 45 | 46 | sudo ifconfig $MY_RX down 47 | sudo iw dev $MY_RX set monitor otherbss fcsfail 48 | sudo ifconfig $MY_RX up 49 | sudo iwconfig $MY_RX channel $MY_WIFI_CHANNEL 50 | #sudo iwconfig $MY_RX channel "6" HT40+ 51 | 52 | ## test with multiple RXes 53 | #sudo ifconfig $MY_RX_SECONDARY down 54 | #sudo iw dev $MY_RX_SECONDARY set monitor otherbss fcsfail 55 | #sudo ifconfig $MY_RX_SECONDARY up 56 | #sudo iwconfig $MY_RX_SECONDARY channel 6 57 | 58 | 59 | # $WFB_FOLDER/wfb_tx -k $FEC_K -n $FEC_N -u 6000 -p 60 -M 7 -K $WFB_FOLDER/drone.key $MY_TX 60 | #xterm -hold -e $WFB_FOLDER/wfb_tx -k $FEC_K -p $FEC_PERCENTAGE -u 5600 -r $RADIO_PORT -M 6 -B 20 -K $WFB_FOLDER/drone.key $MY_TX & 61 | 62 | $WFB_FOLDER/wfb_rx -c 127.0.0.1 -u 5601 -r $RADIO_PORT $MY_RX 63 | 64 | #other usefull commands: 65 | #sudo iw dev 66 | #nc -u localhost 6000 67 | #nc -u -l localhost 6100 68 | -------------------------------------------------------------------------------- /latencyTesting/enable_monitor_mode.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # enable monitor mode and sets the card 3 | # to the right wifi channel (for listening OR injecting) 4 | # takes 2 parameters - the wifi card interface name and the wanted wifi channel 5 | 6 | # wifi card is first param 7 | MY_WIFI_CARD=$1 8 | # wifi channel is the second param 9 | MY_WIFI_CHANNEL=$2 10 | 11 | sudo rfkill unblock wifi 12 | #sudo killall ifplugd #stop management of interface 13 | 14 | sudo ifconfig $MY_WIFI_CARD down 15 | sudo iw dev $MY_WIFI_CARD set monitor otherbss fcsfail 16 | sudo ifconfig $MY_WIFI_CARD up 17 | sudo iwconfig $MY_WIFI_CARD channel $MY_WIFI_CHANNEL 18 | #sudo iw dev $MY_TX set channel "6" HT40+ 19 | #sudo iwconfig $MY_TX rts off 20 | 21 | echo "Monitor mode enabled on card $WIFI_CARD set to channel $MY_WIFI_CHANNEL" -------------------------------------------------------------------------------- /latencyTesting/startRxSimple.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Given a PC with 2 wifi cards connected that support monitor mode, 3 | # This starts the tx on one of them and the rx on the other one 4 | 5 | TAOBAO="wlx00e0863200b9" #Taobao card 6 | ASUS="wlx244bfeb71c05" #ASUS card 7 | #ASUS2="wlxac9e175ac9e8" #ASUS card 2 8 | ASUS2="wlxac9e17596103" 9 | 10 | #MY_TX="wlp3s0" 11 | #MY_TX="wlxc4e984126183" 12 | #MY_TX="wlx000f00460445" # ALFA card 13 | #MY_TX="wlx6cfdb9b2a156" #PW-DN421 14 | #MY_TX="wlx0018e7bd24db" 15 | #MY_RX="wlx0018e7bd24db" #Ralink card aliexpress 16 | #MY_RX="wlxc4e9840e3cbe" #tp-link rx 17 | #MY_RX_SECONDARY="wlxc4e984126183" 18 | 19 | #MY_TX=$ASUS 20 | MY_RX=$ASUS 21 | #MY_TX="wlan0" #rpi testing 22 | #MY_RX="wlan1" #rpi testing 23 | 24 | WFB_FOLDER="/home/consti10/Desktop/wifibroadcast/build" 25 | #WFB_FOLDER="/home/pi/Desktop/wifibroadcast" 26 | 27 | FEC_K=0 28 | FEC_PERCENTAGE=0 29 | 30 | RADIO_PORT=60 31 | 32 | MY_WIFI_CHANNEL=149 #5ghz channel 33 | #MY_WIFI_CHANNEL=13 #2.4ghz channel 34 | 35 | sudo rfkill unblock wifi 36 | #sudo killall ifplugd #stop management of interface 37 | 38 | #sudo ifconfig $MY_TX down 39 | #sudo iw dev $MY_TX set monitor otherbss fcsfail 40 | #sudo ifconfig $MY_TX up 41 | #sudo iwconfig $MY_TX channel $MY_WIFI_CHANNEL 42 | #sudo iw dev $MY_TX set channel "6" HT40+ 43 | #sudo iwconfig $MY_TX rts off 44 | 45 | sudo ifconfig $MY_RX down 46 | sudo iw dev $MY_RX set monitor otherbss fcsfail 47 | sudo ifconfig $MY_RX up 48 | sudo iwconfig $MY_RX channel $MY_WIFI_CHANNEL 49 | #sudo iwconfig $MY_RX channel "6" HT40+ 50 | 51 | ## test with multiple RXes 52 | #sudo ifconfig $MY_RX_SECONDARY down 53 | #sudo iw dev $MY_RX_SECONDARY set monitor otherbss fcsfail 54 | #sudo ifconfig $MY_RX_SECONDARY up 55 | #sudo iwconfig $MY_RX_SECONDARY channel 6 56 | 57 | 58 | # $WFB_FOLDER/wfb_tx -k $FEC_K -n $FEC_N -u 6000 -p 60 -M 7 -K $WFB_FOLDER/drone.key $MY_TX 59 | #xterm -hold -e $WFB_FOLDER/wfb_tx -k $FEC_K -p $FEC_PERCENTAGE -u 5600 -r $RADIO_PORT -M 6 -B 20 -K $WFB_FOLDER/drone.key $MY_TX & 60 | 61 | $WFB_FOLDER/wfb_rx -c 127.0.0.1 -u 5601 -r $RADIO_PORT $MY_RX 62 | 63 | #other usefull commands: 64 | #sudo iw dev 65 | #nc -u localhost 6000 66 | #nc -u -l localhost 6100 67 | -------------------------------------------------------------------------------- /latencyTesting/startRxVideo.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Given a PC with 2 wifi cards connected that support monitor mode, 3 | # This starts the tx on one of them and the rx on the other one 4 | 5 | TAOBAO="wlx00e0863200b9" #Taobao card 6 | ASUS="wlx244bfeb71c05" #ASUS card 7 | 8 | 9 | MY_RX=$TAOBAO 10 | #MY_RX="wlan0" #rpi testing 11 | 12 | WFB_FOLDER="/home/consti10/Desktop/wifibroadcast" 13 | #WFB_FOLDER="/home/pi/Desktop/wifibroadcast" 14 | 15 | MY_WIFI_CHANNEL=149 #5ghz channel 16 | #MY_WIFI_CHANNEL=13 #2.4ghz channel 17 | 18 | sudo rfkill unblock wifi 19 | #sudo killall ifplugd #stop management of interface 20 | 21 | sudo ifconfig $MY_RX down 22 | sudo iw dev $MY_RX set monitor otherbss fcsfail 23 | sudo ifconfig $MY_RX up 24 | sudo iwconfig $MY_RX channel $MY_WIFI_CHANNEL 25 | #sudo iw dev $MY_TX set channel "6" HT40+ 26 | #sudo iwconfig $MY_TX rts off 27 | 28 | $WFB_FOLDER/wfb_rx -u 6100 -r 60 -K $WFB_FOLDER/gs.key $MY_RX 29 | 30 | 31 | -------------------------------------------------------------------------------- /latencyTesting/startTxAndRxOnMyPc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Given a PC with 2 wifi cards connected that support monitor mode, 3 | # This starts the tx on one of them and the rx on the other one 4 | 5 | TAOBAO="wlx00e0863200b9" #Taobao card 6 | ASUS="wlx244bfeb71c05" #ASUS card 7 | #ASUS2="wlxac9e175ac9e8" #ASUS card 2 8 | ASUS2="wlxac9e17596103" 9 | 10 | #MY_TX="wlp3s0" 11 | #MY_TX="wlxc4e984126183" 12 | #MY_TX="wlx000f00460445" # ALFA card 13 | #MY_TX="wlx6cfdb9b2a156" #PW-DN421 14 | #MY_TX="wlx0018e7bd24db" 15 | #MY_RX="wlx0018e7bd24db" #Ralink card aliexpress 16 | #MY_RX="wlxc4e9840e3cbe" #tp-link rx 17 | #MY_RX_SECONDARY="wlxc4e984126183" 18 | 19 | MY_TX=$ASUS 20 | MY_RX=$ASUS2 21 | #MY_TX="wlan0" #rpi testing 22 | #MY_RX="wlan1" #rpi testing 23 | 24 | WFB_FOLDER="/home/consti10/Desktop/wifibroadcast/build" 25 | #WFB_FOLDER="/home/pi/Desktop/wifibroadcast" 26 | 27 | FEC_K=0 28 | FEC_PERCENTAGE=0 29 | 30 | RADIO_PORT=60 31 | 32 | MY_WIFI_CHANNEL=149 #5ghz channel 33 | #MY_WIFI_CHANNEL=13 #2.4ghz channel 34 | 35 | sudo rfkill unblock wifi 36 | #sudo killall ifplugd #stop management of interface 37 | 38 | sudo ifconfig $MY_TX down 39 | sudo iw dev $MY_TX set monitor otherbss fcsfail 40 | sudo ifconfig $MY_TX up 41 | sudo iwconfig $MY_TX channel $MY_WIFI_CHANNEL 42 | #sudo iw dev $MY_TX set channel "6" HT40+ 43 | #sudo iwconfig $MY_TX rts off 44 | 45 | sudo ifconfig $MY_RX down 46 | sudo iw dev $MY_RX set monitor otherbss fcsfail 47 | sudo ifconfig $MY_RX up 48 | sudo iwconfig $MY_RX channel $MY_WIFI_CHANNEL 49 | #sudo iwconfig $MY_RX channel "6" HT40+ 50 | 51 | ## test with multiple RXes 52 | #sudo ifconfig $MY_RX_SECONDARY down 53 | #sudo iw dev $MY_RX_SECONDARY set monitor otherbss fcsfail 54 | #sudo ifconfig $MY_RX_SECONDARY up 55 | #sudo iwconfig $MY_RX_SECONDARY channel 6 56 | 57 | 58 | # $WFB_FOLDER/wfb_tx -k $FEC_K -n $FEC_N -u 6000 -p 60 -M 7 -K $WFB_FOLDER/drone.key $MY_TX 59 | xterm -hold -e $WFB_FOLDER/wfb_tx -k $FEC_K -p $FEC_PERCENTAGE -u 5600 -r $RADIO_PORT -M 6 -B 20 -K $WFB_FOLDER/drone.key $MY_TX & 60 | 61 | $WFB_FOLDER/wfb_rx -c 127.0.0.1 -u 5601 -r $RADIO_PORT -K $WFB_FOLDER/gs.key $MY_RX 62 | 63 | #other usefull commands: 64 | #sudo iw dev 65 | #nc -u localhost 6000 66 | #nc -u -l localhost 6100 67 | -------------------------------------------------------------------------------- /latencyTesting/startTxAndRxOnMyPc2.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Given a PC with 2 wifi cards connected that support monitor mode, 3 | # This starts the tx on one of them and the rx on the other one 4 | # Assuming tx and rx are already set into monitor mode ! 5 | 6 | TAOBAO="wlx00e0863200b9" #Taobao card 7 | ASUS="wlx244bfeb71c05" #ASUS card 8 | 9 | #MY_TX="wlp3s0" 10 | #MY_TX="wlxc4e984126183" 11 | #MY_TX="wlx000f00460445" # ALFA card 12 | #MY_TX="wlx6cfdb9b2a156" #PW-DN421 13 | #MY_TX="wlx0018e7bd24db" 14 | #MY_RX="wlx0018e7bd24db" #Ralink card aliexpress 15 | #MY_RX="wlxc4e9840e3cbe" #tp-link rx 16 | #MY_RX_SECONDARY="wlxc4e984126183" 17 | 18 | MY_TX=$ASUS 19 | MY_RX=$TAOBAO 20 | #MY_TX="wlan0" #rpi testing 21 | #MY_RX="wlan1" #rpi testing 22 | 23 | WFB_FOLDER="/home/consti10/Desktop/wifibroadcast" 24 | #WFB_FOLDER="/home/pi/Desktop/wifibroadcast" 25 | 26 | FEC_K=8 27 | FEC_PERCENTAGE=50 28 | 29 | RADIO_PORT=61 30 | 31 | MY_WIFI_CHANNEL=149 #5ghz channel 32 | 33 | xterm -hold -e $WFB_FOLDER/wfb_tx -k $FEC_K -p $FEC_PERCENTAGE -u 6001 -r $RADIO_PORT -M 5 -B 20 -K $WFB_FOLDER/drone.key $MY_TX & 34 | 35 | $WFB_FOLDER/wfb_rx -c 127.0.0.1 -u 6101 -r $RADIO_PORT -K $WFB_FOLDER/gs.key $MY_RX 36 | 37 | -------------------------------------------------------------------------------- /latencyTesting/startTxAndRxOnMyPcInBidirectionalMode.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Given a PC with 2 wifi cards connected that support monitor mode, 3 | # This starts the tx on one of them and the rx on the other one 4 | 5 | TAOBAO="wlx00e0863200b9" #Taobao card 6 | ASUS="wlx244bfeb71c05" #ASUS card 7 | 8 | MY_TX=$ASUS 9 | MY_RX=$TAOBAO 10 | 11 | WFB_FOLDER="/home/consti10/Desktop/wifibroadcast" 12 | 13 | FEC_K=0 14 | FEC_PERCENTAGE=0 15 | 16 | sudo rfkill unblock wifi 17 | #sudo killall ifplugd #stop management of interface 18 | 19 | sudo ifconfig $MY_TX down 20 | sudo iw dev $MY_TX set monitor otherbss fcsfail 21 | sudo ifconfig $MY_TX up 22 | sudo iwconfig $MY_TX channel 6 23 | #sudo iw dev $MY_TX set channel "6" HT40+ 24 | 25 | sudo ifconfig $MY_RX down 26 | sudo iw dev $MY_RX set monitor otherbss fcsfail 27 | sudo ifconfig $MY_RX up 28 | sudo iwconfig $MY_RX channel 6 29 | #sudo iwconfig $MY_RX channel "6" HT40+ 30 | 31 | 32 | xterm -hold -e $WFB_FOLDER/wfb_tx -k $FEC_K -p $FEC_PERCENTAGE -u 6000 -r 60 -M 7 -K $WFB_FOLDER/drone.key $MY_TX & 33 | 34 | xterm -hold -e $WFB_FOLDER/wfb_rx -u 6100 -r 60 -K $WFB_FOLDER/gs.key $MY_RX & 35 | 36 | xterm -hold -e $WFB_FOLDER/wfb_tx -k $FEC_K -p $FEC_PERCENTAGE -u 6001 -r 70 -M 7 -K $WFB_FOLDER/drone.key $MY_RX & 37 | 38 | xterm -hold -e $WFB_FOLDER/wfb_rx -u 6101 -r 70 -K $WFB_FOLDER/gs.key $MY_TX 39 | 40 | -------------------------------------------------------------------------------- /latencyTesting/startTxSimple.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Given a PC with 2 wifi cards connected that support monitor mode, 3 | # This starts the tx on one of them and the rx on the other one 4 | 5 | TAOBAO="wlx00e0863200b9" #Taobao card 6 | ASUS="wlx244bfeb71c05" #ASUS card 7 | #ASUS2="wlxac9e175ac9e8" #ASUS card 2 8 | ASUS2="wlxac9e17596103" 9 | 10 | #MY_TX="wlp3s0" 11 | #MY_TX="wlxc4e984126183" 12 | #MY_TX="wlx000f00460445" # ALFA card 13 | #MY_TX="wlx6cfdb9b2a156" #PW-DN421 14 | #MY_TX="wlx0018e7bd24db" 15 | #MY_RX="wlx0018e7bd24db" #Ralink card aliexpress 16 | #MY_RX="wlxc4e9840e3cbe" #tp-link rx 17 | #MY_RX_SECONDARY="wlxc4e984126183" 18 | 19 | #MY_TX=$ASUS 20 | #MY_RX=$ASUS2 21 | MY_TX="wlan1" #rpi testing 22 | MY_RX="wlan2" #rpi testing 23 | 24 | #WFB_FOLDER="/home/consti10/Desktop/wifibroadcast/build" 25 | WFB_FOLDER="/home/openhd/wifibroadcast/build" 26 | #WFB_FOLDER="/home/pi/Desktop/wifibroadcast" 27 | 28 | FEC_K=0 29 | FEC_PERCENTAGE=0 30 | 31 | RADIO_PORT=60 32 | 33 | MY_WIFI_CHANNEL=149 #5ghz channel 34 | #MY_WIFI_CHANNEL=13 #2.4ghz channel 35 | 36 | sudo rfkill unblock wifi 37 | #sudo killall ifplugd #stop management of interface 38 | 39 | sudo ifconfig $MY_TX down 40 | sudo iw dev $MY_TX set monitor otherbss fcsfail 41 | sudo ifconfig $MY_TX up 42 | sudo iwconfig $MY_TX channel $MY_WIFI_CHANNEL 43 | #sudo iw dev $MY_TX set channel "6" HT40+ 44 | #sudo iwconfig $MY_TX rts off 45 | 46 | #sudo ifconfig $MY_RX down 47 | #sudo iw dev $MY_RX set monitor otherbss fcsfail 48 | #sudo ifconfig $MY_RX up 49 | #sudo iwconfig $MY_RX channel $MY_WIFI_CHANNEL 50 | #sudo iwconfig $MY_RX channel "6" HT40+ 51 | 52 | ## test with multiple RXes 53 | #sudo ifconfig $MY_RX_SECONDARY down 54 | #sudo iw dev $MY_RX_SECONDARY set monitor otherbss fcsfail 55 | #sudo ifconfig $MY_RX_SECONDARY up 56 | #sudo iwconfig $MY_RX_SECONDARY channel 6 57 | 58 | 59 | # $WFB_FOLDER/wfb_tx -k $FEC_K -n $FEC_N -u 6000 -p 60 -M 7 -K $WFB_FOLDER/drone.key $MY_TX 60 | $WFB_FOLDER/wfb_tx -k $FEC_K -p $FEC_PERCENTAGE -u 5600 -r $RADIO_PORT -M 6 -B 20 $MY_TX 61 | 62 | #$WFB_FOLDER/wfb_rx -c 127.0.0.1 -u 5601 -r $RADIO_PORT -K $WFB_FOLDER/gs.key $MY_RX 63 | 64 | #other usefull commands: 65 | #sudo iw dev 66 | #nc -u localhost 6000 67 | #nc -u -l localhost 6100 68 | -------------------------------------------------------------------------------- /latencyTesting/startTxVideo.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Given a PC with 2 wifi cards connected that support monitor mode, 3 | # This starts the tx on one of them and the rx on the other one 4 | 5 | TAOBAO="wlx00e0863200b9" #Taobao card 6 | ASUS="wlx244bfeb71c05" #ASUS card 7 | 8 | 9 | MY_TX=$TAOBAO 10 | #MY_TX="wlan0" #rpi testing 11 | 12 | WFB_FOLDER="/home/consti10/Desktop/wifibroadcast" 13 | #WFB_FOLDER="/home/pi/Desktop/wifibroadcast" 14 | 15 | FEC_K="h264" 16 | FEC_PERCENTAGE=50 17 | 18 | MY_WIFI_CHANNEL=149 #5ghz channel 19 | #MY_WIFI_CHANNEL=13 #2.4ghz channel 20 | 21 | sudo rfkill unblock wifi 22 | #sudo killall ifplugd #stop management of interface 23 | 24 | sudo ifconfig $MY_TX down 25 | sudo iw dev $MY_TX set monitor otherbss fcsfail 26 | sudo ifconfig $MY_TX up 27 | sudo iwconfig $MY_TX channel $MY_WIFI_CHANNEL 28 | #sudo iw dev $MY_TX set channel "6" HT40+ 29 | #sudo iwconfig $MY_TX rts off 30 | 31 | $WFB_FOLDER/wfb_tx -k $FEC_K -p $FEC_PERCENTAGE -u 6000 -r 60 -M 5 -B 20 -K $WFB_FOLDER/drone.key $MY_TX 32 | 33 | 34 | -------------------------------------------------------------------------------- /latencyTesting/test_2_cards.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # This script enables monitor mode on 2 wifi cards (both connected to the same pc) and 3 | # starts injecting generated packets on the tx card. At the same time, the packets received 4 | # on the rx are validated. This is a simple test to make sure that injecting and receiving packets works, 5 | # and that the received packets have the right content. 6 | 7 | ASUS="wlx244bfeb71c05" #ASUS card 8 | ASUS2="wlxac9e175ac9e8" #ASUS card 2 9 | 10 | MY_RX=$ASUS 11 | MY_TX=$ASUS2 12 | 13 | #MY_WIFI_CHANNEL=149 #5ghz channel 14 | MY_WIFI_CHANNEL=153 #5ghz channel 15 | #MY_WIFI_CHANNEL=13 #2.4ghz channel 16 | 17 | #WFB_FOLDER="/home/consti10/Desktop/wifibroadcast" 18 | WFB_FOLDER="/home/consti10/Desktop/Open.HD/OpenHD/lib/wifibroadcast/cmake-build-debug" 19 | #WFB_FOLDER="/home/pi/Desktop/wifibroadcast" 20 | 21 | # enable monitor mode on rx card, start wfb_rx 22 | sh ./enable_monitor_mode.sh $MY_RX $MY_WIFI_CHANNEL 23 | 24 | xterm -hold -e $WFB_FOLDER/wfb_rx -u 6200 -r 60 $MY_RX & 25 | 26 | 27 | # enable monitor mode on tx card, start wfb_tx 28 | sh ./enable_monitor_mode.sh $MY_TX $MY_WIFI_CHANNEL 29 | 30 | xterm -hold -e $WFB_FOLDER/wfb_tx -u 6000 -r 60 -M 5 -B 20 $MY_TX & 31 | 32 | 33 | # validate incoming packets 34 | xterm -hold -e $WFB_FOLDER/udp_generator_validator -u 6200 -v 1 -t 30 & 35 | 36 | # start the generator 37 | $WFB_FOLDER/udp_generator_validator -u 6000 -p 100 -t 30 -------------------------------------------------------------------------------- /other/neon_compiler_test/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS = -g 3 | 4 | example: example.cpp 5 | $(CC) $(CFLAGS) -o example example.cpp 6 | 7 | clean: 8 | $(RM) example 9 | -------------------------------------------------------------------------------- /papers/Analysis of Injection Capabilities and Media Access of IEEE 802.11 Hardware in Monitor Mode.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenHD/wifibroadcast/99e53e7ea6dfafff85999504481d7cf26e3ee749/papers/Analysis of Injection Capabilities and Media Access of IEEE 802.11 Hardware in Monitor Mode.pdf -------------------------------------------------------------------------------- /papers/CollectionOfLinks.txt: -------------------------------------------------------------------------------- 1 | // Other links that might be usefully 2 | https://github.com/vanhoefm/modwifi-tools/blob/master/ieee80211header.h 3 | https://github.com/qca/open-ath9k-htc-firmware 4 | https://befinitiv.wordpress.com/2015/02/22/finding-the-right-wifi-dongle-and-patching-its-kernel-driver-and-firmware/ 5 | https://www.oreilly.com/library/view/80211-wireless-networks/0596100523/ch04.html 6 | 7 | // the firmware patches: 8 | https://github.com/qca/open-ath9k-htc-firmware/blob/d0ad09603867beab49ead2536b2239be43d6eb42/target_firmware/wlan/if_ath.c#L119 9 | 10 | If I read it correctly, here the rate is set: 11 | https://github.com/qca/open-ath9k-htc-firmware/blob/d0ad09603867beab49ead2536b2239be43d6eb42/target_firmware/wlan/if_ath.c#L1938 12 | 13 | // and here is the function that is called 14 | https://github.com/qca/open-ath9k-htc-firmware/blob/d0ad09603867beab49ead2536b2239be43d6eb42/target_firmware/wlan/if_ath.c#L119 15 | 16 | 17 | https://github.com/aircrack-ng/rtl8812au/issues/116 -------------------------------------------------------------------------------- /papers/ExcerptsFromTelegram.txt: -------------------------------------------------------------------------------- 1 | Here are some excerpts of the telegram chat during development such that stuff doesn't get lost: 2 | 3 | To sum it up, here is how the rx ring should now behave: 4 | ( and can behave at its best) 5 | Size of 1: 6 | Same behaviour as if you were using no rx ring. 7 | Best case- latency of packet travel time 8 | Worst case- latency of infinity. 9 | ( assuming you get 1 block that is not fully recoverable and then the link is suddenly fully cut) 10 | However, the worst case is not practical - it is actually the latency until you get the the first packet refering to the next block. 11 | Because in this case the pipeline gets flushed due to size of 1. 12 | Size of 2 ( or N): 13 | Best case - latency of packet travel time 14 | Worst case- latency of infinity. 15 | Here the worst-case practical latency is 16 | "time until you either receive the first fully recoverable block" or "time until you receive data for block with idx+2" if N==2 and so on. 17 | With N==3 this increases to idx+3 , ... 18 | As you can see, decreasing the rx ring size can decrease the likeniness of stuck packets "aka latency of infinity" but not fully fix it. 19 | So the only sensible option is to add a 3rd parameter - a timeout on the receiver. 20 | If data doesn't arrive for n milliseconds, you can say: 21 | Hey, I don't think that after 10ms we will get a new packet that helps us with the old blocks in the ring. 22 | It is most likely a new block by itself. 23 | And flush the pipeline. 24 | But if you use a timeout too small you loose on packet recovery. 25 | So for video we are probably best with "rx ring size as small as possible" and a timeout that is close to the video frame time. 26 | With telemetry down we can either use FEC K,N == 1,2 to avoid the problem of stuck packets entirely or use a smaller timeout. 27 | And for RX up we should probably skip FEC entirely. ( I added K==0 for that) or also go with K,N==1,2 which is the same as sending each packet twice. 28 | Also,when using a small timeout we must not forget that it's overhead is not to underestimate, since even though we do nothing in case of no data the cpu still has to swithch to the current context to determine that. 29 | 30 | 31 | So I've added a parameter -f (flush pipeline) to the rx. 32 | If no data is received for more than f milliseconds, the pipeline is fully flushed. 33 | To use a smaller time intervall we have to fulfill the premise "video NALUs are always aligned with one FEC block" or add stuff to the tx that flushes the fec encoder queue,too. 34 | Without that the flush intervall on the rx cannot be smaller than "video frame time + max deviation between wifi packets on rx". 35 | Therefore I defaultet to 40ms , which is quite a lot (40ms of max latency on the link) 36 | However, even without stuff like that on the tx you could use a really small flush intervall - like 10ms. 37 | If the there is no packet loss in the last block, you won't loose any packets 38 | 39 | So I've added a flush parameter to the tx, too. 40 | If data doesn'arrive on the tx for n ms, if the current block is not finished, it adds as many "empty packets" to the pipeline until the fec packets can be calculated and the block is finished. 41 | On the tx, the flush parameter can be much smaller for video independent of the fps. 42 | For example, 2ms for video. Defaults to 40ms too. 43 | 44 | With 2ms on the TX you can go for something much smaller on the RX. Like 10ms. This would give you 8ms to account for deviation(s) between your multiple RX cards. 45 | -------------------------------------------------------------------------------- /papers/fec/10.1.1.219.3226.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenHD/wifibroadcast/99e53e7ea6dfafff85999504481d7cf26e3ee749/papers/fec/10.1.1.219.3226.pdf -------------------------------------------------------------------------------- /papers/fec/10.1.1.80.3513.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenHD/wifibroadcast/99e53e7ea6dfafff85999504481d7cf26e3ee749/papers/fec/10.1.1.80.3513.pdf -------------------------------------------------------------------------------- /papers/fec/NetCod2014-GuRiUt-EfficientGfArithmetics.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenHD/wifibroadcast/99e53e7ea6dfafff85999504481d7cf26e3ee749/papers/fec/NetCod2014-GuRiUt-EfficientGfArithmetics.pdf -------------------------------------------------------------------------------- /papers/mimo_for_dummies.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenHD/wifibroadcast/99e53e7ea6dfafff85999504481d7cf26e3ee749/papers/mimo_for_dummies.pdf -------------------------------------------------------------------------------- /run_clang_format.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # OpenHD clang-format checking. 4 | # Performed steps: 5 | # Step1: Generate list of all .cpp / .h / .hpp files of this project 6 | # (excluding subdirectories) 7 | # Step 2: Run clang-format 8 | # Arguments: run with 'f' to fix things (otherwise, default, only check and report 9 | # error if clang-format finds any issues 10 | 11 | 12 | function append_all_sources_headers() { 13 | # We use .h, .cpp and .hpp in OpenHD 14 | TMP_FILE_LIST="$(find "$1" | grep -E ".*(\.cpp|\.h|\.hpp)$")" 15 | #TMP_FILE_LIST+='\n' 16 | FILE_LIST+=$'\n' 17 | FILE_LIST+=$TMP_FILE_LIST 18 | } 19 | 20 | THIS_PATH="$(realpath "$0")" 21 | THIS_DIR="$(dirname "$THIS_PATH")" 22 | 23 | append_all_sources_headers "$THIS_DIR/wifibroadcast/src" 24 | append_all_sources_headers "$THIS_DIR/wifibroadcast/executables" 25 | 26 | echo "Files found to format = \n\"\"\"\n$FILE_LIST\n\"\"\"" 27 | 28 | # Checks for clang-format issues and returns error if they exist 29 | function check_warning(){ 30 | clang-format --dry-run --Werror --verbose -i --style=file $FILE_LIST 31 | 32 | if [ "$?" -eq "0" ]; then 33 | echo "Everything formatted correctly" 34 | else 35 | echo "There are formatting errors ! Please fix first." 36 | exit 1 37 | fi 38 | } 39 | 40 | # fixes any issues (re-formats everything) 41 | function fix_warnings() { 42 | clang-format --verbose -i --style=file $FILE_LIST 43 | } 44 | 45 | if [ "$1" == "f" ]; then 46 | echo "Fixing warnings" 47 | fix_warnings 48 | else 49 | echo "Checking warnings" 50 | check_warning 51 | fi -------------------------------------------------------------------------------- /rv1126/enable_monitor_mode.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # same as enable_monitor_mode but without sudo (aka must be running already as root) 3 | # reason: buildroot doesn't know sudo, it is root by default 4 | 5 | # wifi card is first param 6 | MY_WIFI_CARD=$1 7 | # wifi channel is the second param 8 | MY_WIFI_CHANNEL=$2 9 | 10 | rfkill unblock wifi 11 | #sudo killall ifplugd #stop management of interface 12 | 13 | ifconfig $MY_WIFI_CARD down 14 | iw dev $MY_WIFI_CARD set monitor otherbss fcsfail 15 | ifconfig $MY_WIFI_CARD up 16 | iwconfig $MY_WIFI_CARD channel $MY_WIFI_CHANNEL 17 | #sudo iw dev $MY_TX set channel "6" HT40+ 18 | #sudo iwconfig $MY_TX rts off 19 | 20 | echo "Monitor mode enabled on card $WIFI_CARD set to channel $MY_WIFI_CHANNEL" -------------------------------------------------------------------------------- /rv1126/start_rx_simple.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Given a PC with 2 wifi cards connected that support monitor mode, 3 | # This starts the tx on one of them and the rx on the other one 4 | 5 | TAOBAO="wlx00e0863200b9" #Taobao card 6 | ASUS="wlx244bfeb71c05" #ASUS card 7 | 8 | 9 | MY_RX=$TAOBAO 10 | 11 | MY_WIFI_CHANNEL=149 #5ghz channel 12 | #MY_WIFI_CHANNEL=13 #2.4ghz channel 13 | 14 | WFB_FOLDER="/home/consti10/Desktop/wifibroadcast" 15 | #WFB_FOLDER="/home/pi/Desktop/wifibroadcast" 16 | 17 | 18 | sh ./enable_monitor_mode.sh $MY_RX $MY_WIFI_CHANNEL 19 | 20 | xterm -hold -e $WFB_FOLDER/wfb_rx -u 5600 -r 60 $MY_RX & 21 | 22 | $WFB_FOLDER/udp_generator_validator -u 5600 -v 1 -------------------------------------------------------------------------------- /rv1126/start_tx_simple.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Given a PC with 2 wifi cards connected that support monitor mode, 3 | # This starts the tx on one of them and the rx on the other one 4 | 5 | TAOBAO="wlx00e0863200b9" #Taobao card 6 | ASUS="wlx244bfeb71c05" #ASUS card 7 | RV1126="wlan0" 8 | 9 | #MY_TX=$TAOBAO 10 | MY_TX=$RV1126 11 | 12 | MY_WIFI_CHANNEL=149 #5ghz channel 13 | #MY_WIFI_CHANNEL=13 #2.4ghz channel 14 | 15 | FEC_K=1 16 | 17 | sh ./enable_monitor_mode.sh $MY_TX $MY_WIFI_CHANNEL 18 | 19 | /oem/usr/bin/wfb_tx -u 5600 -r 60 -M 5 -B 20 -k $FEC_K $MY_TX 20 | -------------------------------------------------------------------------------- /scripts/enable_monitor_mode.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Simple script to enable monitor mode on a wifi card 4 | # Intended to be used with executables/example_hello.cpp 5 | 6 | # Write your own card name here !! 7 | MY_WIFI_CARD=$1 8 | 9 | echo "Enabling monitor mode on card: $MY_WIFI_CARD"; 10 | 11 | # tell network manager to keep its hands from this card 12 | # If OS doesn't use nm, you might have to find suitable replacement !! 13 | sudo nmcli device set $MY_WIFI_CARD managed no 14 | 15 | # might be needed / might not be needed 16 | sudo rfkill unblock all 17 | 18 | sleep 1s 19 | 20 | sudo ifconfig $MY_WIFI_CARD down 21 | sudo iw dev $MY_WIFI_CARD set monitor otherbss fcsfail 22 | sudo ifconfig $MY_WIFI_CARD up 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /scripts/set_freq.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Simple script to set a default wifi frequency on a card in monitor mode 4 | # 5 | 6 | # wifi card is first param 7 | MY_WIFI_CARD=$1 8 | 9 | # frequency is second param 10 | MY_WIFI_FREQ_MHZ=$2 11 | 12 | # channel width is third param 13 | MY_WIFI_CHANNEL_WIDTH=$3 14 | 15 | echo "Setting $MY_WIFI_CARD to $MY_WIFI_FREQ_MHZ at $MY_WIFI_CHANNEL_WIDTH" 16 | 17 | sudo iw dev $MY_WIFI_CARD set freq $MY_WIFI_FREQ_MHZ $MY_WIFI_CHANNEL_WIDTH -------------------------------------------------------------------------------- /scripts/simple_2G.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Simple script to enable monitor mode and set same frequency all together 4 | # Intended to be used with executables/example_hello.cpp 5 | 6 | if [ $# -eq 0 ] 7 | then 8 | echo "Please specify the card intended for wifibroadcast" 9 | exit -1 10 | fi 11 | 12 | # !! Need to pass card 13 | MY_WIFI_CARD=$1 14 | 15 | 16 | sh ./enable_monitor_mode.sh $MY_WIFI_CARD 17 | 18 | # Should work on most card(s) - 2412Mhz at HT20 (20Mhz channel width) 19 | sh ./set_freq.sh $MY_WIFI_CARD 2412 HT20 20 | 21 | 22 | -------------------------------------------------------------------------------- /scripts/simple_5G.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Simple script to enable monitor mode and set same frequency all together 4 | # Intended to be used with executables/example_hello.cpp 5 | 6 | if [ $# -eq 0 ] 7 | then 8 | echo "Please specify the card intended for wifibroadcast" 9 | exit -1 10 | fi 11 | 12 | # !! Need to pass card 13 | MY_WIFI_CARD=$1 14 | 15 | 16 | sh ./enable_monitor_mode.sh $MY_WIFI_CARD 17 | 18 | # Should work on most card(s) - 5180Mhz at HT20 (20Mhz channel width) 19 | sh ./set_freq.sh $MY_WIFI_CARD 5180 HT20 20 | 21 | 22 | -------------------------------------------------------------------------------- /scripts/simple_consti_pc.sh: -------------------------------------------------------------------------------- 1 | #bin/bash 2 | # For my pc, since I always use the same 4 cards for testing (2*RTL8812AU, 2xAR9271) that report themselves with fixed interface names 3 | # I created this script for easier setup 4 | sudo ./simple_2G.sh wlx6cfdb9b2a150 5 | sudo ./simple_2G.sh wlxc4e984126183 6 | 7 | sudo ./simple_5G.sh wlxac9e17596103 8 | sudo ./simple_5G.sh wlx0492268830e5 -------------------------------------------------------------------------------- /wifibroadcast/cmake/FindPCAP.cmake: -------------------------------------------------------------------------------- 1 | # from https://github.com/bro/cmake/blob/master/FindPCAP.cmake 2 | # - Try to find libpcap include dirs and libraries 3 | # 4 | # Usage of this module as follows: 5 | # 6 | # find_package(PCAP) 7 | # 8 | # Variables used by this module, they can change the default behaviour and need 9 | # to be set before calling find_package: 10 | # 11 | # PCAP_ROOT_DIR Set this variable to the root installation of 12 | # libpcap if the module has problems finding the 13 | # proper installation path. 14 | # 15 | # Variables defined by this module: 16 | # 17 | # PCAP_FOUND System has libpcap, include and library dirs found 18 | # PCAP_INCLUDE_DIR The libpcap include directories. 19 | # PCAP_LIBRARY The libpcap library (possibly includes a thread 20 | # library e.g. required by pf_ring's libpcap) 21 | # HAVE_PF_RING If a found version of libpcap supports PF_RING 22 | 23 | find_path(PCAP_ROOT_DIR 24 | NAMES include/pcap.h 25 | ) 26 | 27 | find_path(PCAP_INCLUDE_DIR 28 | NAMES pcap.h 29 | HINTS ${PCAP_ROOT_DIR}/include 30 | ) 31 | 32 | find_library(PCAP_LIBRARY 33 | NAMES pcap 34 | HINTS ${PCAP_ROOT_DIR}/lib 35 | ) 36 | 37 | include(FindPackageHandleStandardArgs) 38 | find_package_handle_standard_args(PCAP DEFAULT_MSG 39 | PCAP_LIBRARY 40 | PCAP_INCLUDE_DIR 41 | ) 42 | 43 | include(CheckCSourceCompiles) 44 | set(CMAKE_REQUIRED_LIBRARIES ${PCAP_LIBRARY}) 45 | check_c_source_compiles("int main() { return 0; }" PCAP_LINKS_SOLO) 46 | set(CMAKE_REQUIRED_LIBRARIES) 47 | 48 | # check if linking against libpcap also needs to link against a thread library 49 | if (NOT PCAP_LINKS_SOLO) 50 | find_package(Threads) 51 | if (THREADS_FOUND) 52 | set(CMAKE_REQUIRED_LIBRARIES ${PCAP_LIBRARY} ${CMAKE_THREAD_LIBS_INIT}) 53 | check_c_source_compiles("int main() { return 0; }" PCAP_NEEDS_THREADS) 54 | set(CMAKE_REQUIRED_LIBRARIES) 55 | endif () 56 | if (THREADS_FOUND AND PCAP_NEEDS_THREADS) 57 | set(_tmp ${PCAP_LIBRARY} ${CMAKE_THREAD_LIBS_INIT}) 58 | list(REMOVE_DUPLICATES _tmp) 59 | set(PCAP_LIBRARY ${_tmp} 60 | CACHE STRING "Libraries needed to link against libpcap" FORCE) 61 | else () 62 | message(FATAL_ERROR "Couldn't determine how to link against libpcap") 63 | endif () 64 | endif () 65 | 66 | include(CheckFunctionExists) 67 | set(CMAKE_REQUIRED_LIBRARIES ${PCAP_LIBRARY}) 68 | check_function_exists(pcap_get_pfring_id HAVE_PF_RING) 69 | check_function_exists(pcap_dump_open_append HAVE_PCAP_DUMP_OPEN_APPEND) 70 | set(CMAKE_REQUIRED_LIBRARIES) 71 | 72 | mark_as_advanced( 73 | PCAP_ROOT_DIR 74 | PCAP_INCLUDE_DIR 75 | PCAP_LIBRARY 76 | ) -------------------------------------------------------------------------------- /wifibroadcast/executables/example_hello.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by consti10 on 01.07.23. 3 | // 4 | 5 | #include "../src/WBStreamRx.h" 6 | #include "../src/WBStreamTx.h" 7 | #include "../src/WBTxRx.h" 8 | #include "../src/wifibroadcast_spdlog.h" 9 | 10 | /** 11 | * Simple demo application that sends out hello messages and listens for hello 12 | * messages. You can run it either on 2 different systems (air unit and ground 13 | * unit) or run it on the same system with 2 different wifi cards for testing. 14 | * 15 | * On success, you should see the air unit talking to the ground unit and vice 16 | * versa. 17 | * 18 | * NOTE: Card(s) need to be in monitor mode & on match on the selected frequency 19 | * ! NOTE: This example is as simple as possible, and therefore doesn't even 20 | * need to use the WBStreamTX / WBStreamRX pair 21 | */ 22 | int main(int argc, char *const *argv) { 23 | std::string card = "wlxac9e17596103"; 24 | bool advanced_debugging = false; 25 | bool is_air = false; 26 | bool air_or_ground_explicitly_specified = false; 27 | int opt; 28 | while ((opt = getopt(argc, argv, "w:agd")) != -1) { 29 | switch (opt) { 30 | case 'w': 31 | card = optarg; 32 | break; 33 | case 'a': 34 | is_air = true; 35 | air_or_ground_explicitly_specified = true; 36 | break; 37 | case 'g': 38 | is_air = false; 39 | air_or_ground_explicitly_specified = true; 40 | break; 41 | case 'd': 42 | advanced_debugging = true; 43 | break; 44 | default: /* '?' */ 45 | fprintf(stderr, 46 | "Example hello %s [-a run as air] [-g run as ground] [-w wifi " 47 | "card to use]\n", 48 | argv[0]); 49 | exit(1); 50 | } 51 | } 52 | if (!air_or_ground_explicitly_specified) { 53 | std::cerr << "Warning - please specify air or ground, air only talks to " 54 | "ground and vice versa" 55 | << std::endl; 56 | } 57 | std::cout << "Running as " << (is_air ? "Air" : "Ground") << " on card " 58 | << card << "\n"; 59 | 60 | // Create the Tx-RX 61 | std::vector cards; 62 | wifibroadcast::WifiCard tmp_card{card, 1}; 63 | cards.push_back(tmp_card); 64 | WBTxRx::Options options_txrx{}; 65 | options_txrx.pcap_rx_set_direction = true; 66 | options_txrx.use_gnd_identifier = !is_air; 67 | if (advanced_debugging) { 68 | options_txrx.log_all_received_validated_packets = true; 69 | options_txrx.advanced_debugging_rx = true; 70 | } 71 | auto radiotap_header_holder = std::make_shared(); 72 | std::shared_ptr txrx = 73 | std::make_shared(cards, options_txrx, radiotap_header_holder); 74 | 75 | txrx->start_receiving(); 76 | 77 | WBTxRx::OUTPUT_DATA_CALLBACK cb = 78 | [](uint64_t nonce, int wlan_index, const uint8_t radioPort, 79 | const uint8_t *data, const std::size_t data_len) { 80 | std::string message((const char *)data, data_len); 81 | fmt::print("Got packet[{}]\n", message); 82 | }; 83 | txrx->rx_register_callback(cb); 84 | 85 | auto lastLog = std::chrono::steady_clock::now(); 86 | int packet_index = 0; 87 | while (true) { 88 | auto message = is_air ? fmt::format("Air says hello {}", packet_index) 89 | : fmt::format("Ground says hello {}", packet_index); 90 | packet_index++; 91 | 92 | // Just use radio port 0 - we don't need multiplexing in this example 93 | // This message is injected on the wifi card 94 | const auto radiotap_header = radiotap_header_holder->thread_safe_get(); 95 | const bool encrypt = false; 96 | txrx->tx_inject_packet(0, (uint8_t *)message.data(), message.size(), 97 | radiotap_header, encrypt); 98 | 99 | std::this_thread::sleep_for(std::chrono::milliseconds(1000)); 100 | const auto elapsed_since_last_log = 101 | std::chrono::steady_clock::now() - lastLog; 102 | if (elapsed_since_last_log > std::chrono::seconds(4)) { 103 | lastLog = std::chrono::steady_clock::now(); 104 | auto txStats = txrx->get_tx_stats(); 105 | auto rxStats = txrx->get_rx_stats(); 106 | auto rx_stats_card0 = txrx->get_rx_stats_for_card(0); 107 | std::cout << txStats << std::endl; 108 | std::cout << rx_stats_card0 << std::endl; 109 | } 110 | } 111 | } -------------------------------------------------------------------------------- /wifibroadcast/executables/example_pollute.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by consti10 on 09.08.23. 3 | // 4 | 5 | #include "../src/HelperSources/RandomBufferPot.hpp" 6 | #include "../src/WBStreamRx.h" 7 | #include "../src/WBStreamTx.h" 8 | #include "../src/WBTxRx.h" 9 | #include "../src/wifibroadcast_spdlog.h" 10 | 11 | /** 12 | * Simple demo application that pollutes a given wifi space 13 | * with a lot of packets with a non-openhd fixed MAC address. 14 | */ 15 | int main(int argc, char *const *argv) { 16 | std::string card = "wlx244bfeb71c05"; 17 | int sleep_time_ms = 10; // 100 pps 18 | int opt; 19 | while ((opt = getopt(argc, argv, "w:s:")) != -1) { 20 | switch (opt) { 21 | case 'w': 22 | card = optarg; 23 | break; 24 | case 's': 25 | sleep_time_ms = std::atoi(optarg); 26 | break; 27 | default: /* '?' */ 28 | show_usage: 29 | fprintf(stderr, 30 | "Example pollute %s [-w wifi card to use] [-s sleep time " 31 | "bwteen packets, in milliseconds]\n", 32 | argv[0]); 33 | exit(1); 34 | } 35 | } 36 | 37 | // Create the Tx-RX 38 | std::vector cards; 39 | wifibroadcast::WifiCard tmp_card{card, 1}; 40 | cards.push_back(tmp_card); 41 | WBTxRx::Options options_txrx{}; 42 | options_txrx.pcap_rx_set_direction = true; 43 | options_txrx.enable_non_openhd_mode = true; 44 | options_txrx.tx_without_pcap = false; 45 | options_txrx.debug_tx_injection_time = true; 46 | options_txrx.tx_without_pcap = true; 47 | 48 | auto radiotap_header_holder = std::make_shared(); 49 | std::shared_ptr txrx = 50 | std::make_shared(cards, options_txrx, radiotap_header_holder); 51 | // We do not need receive in this mode 52 | // txrx->start_receiving(); 53 | wifibroadcast::log::get_default()->debug("Example pollute {}ms", 54 | sleep_time_ms); 55 | 56 | auto lastLog = std::chrono::steady_clock::now(); 57 | int packet_index = 0; 58 | while (true) { 59 | auto message = GenericHelper::createRandomDataBuffer(1024); 60 | packet_index++; 61 | 62 | auto header = radiotap_header_holder->thread_safe_get(); 63 | txrx->tx_inject_packet(0, (uint8_t *)message.data(), message.size(), header, 64 | false); 65 | // About 100pps 66 | std::this_thread::sleep_for(std::chrono::milliseconds(sleep_time_ms)); 67 | 68 | const auto elapsed_since_last_log = 69 | std::chrono::steady_clock::now() - lastLog; 70 | if (elapsed_since_last_log > std::chrono::seconds(4)) { 71 | lastLog = std::chrono::steady_clock::now(); 72 | auto txStats = txrx->get_tx_stats(); 73 | std::cout << txStats << std::endl; 74 | } 75 | } 76 | } -------------------------------------------------------------------------------- /wifibroadcast/executables/socket_helper_test.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by consti10 on 21.04.22. 3 | // 4 | 5 | #include "../src/HelperSources/SocketHelper.hpp" 6 | 7 | static void test_send_and_receive() { 8 | static constexpr auto XPORT = 5600; 9 | std::size_t nReceivedBytes = 0; 10 | SocketHelper::UDPReceiver receiver( 11 | SocketHelper::ADDRESS_LOCALHOST, XPORT, 12 | [&nReceivedBytes](const uint8_t *payload, const std::size_t payloadSize) { 13 | // std::cout<<"Got data\n"; 14 | nReceivedBytes += payloadSize; 15 | }); 16 | receiver.runInBackground(); 17 | // wait a bit to account for OS delay 18 | std::this_thread::sleep_for(std::chrono::seconds(1)); 19 | // SocketHelper::UDPForwarder 20 | // forwarder(SocketHelper::ADDRESS_LOCALHOST,XPORT); 21 | SocketHelper::UDPMultiForwarder forwarder{}; 22 | forwarder.addForwarder(SocketHelper::ADDRESS_LOCALHOST, XPORT); 23 | std::vector data(1024); 24 | std::size_t nForwardedBytes = 0; 25 | for (int i = 0; i < 100; i++) { 26 | forwarder.forwardPacketViaUDP(data.data(), data.size()); 27 | nForwardedBytes += data.size(); 28 | std::this_thread::sleep_for(std::chrono::milliseconds(1)); 29 | } 30 | // wait a bit to account for OS delays 31 | std::this_thread::sleep_for(std::chrono::seconds(1)); 32 | std::cout << "Test end\n"; 33 | receiver.stopBackground(); 34 | std::cout << "N sent bytes:" << nForwardedBytes 35 | << " Received:" << nReceivedBytes << "\n"; 36 | if (nForwardedBytes != nReceivedBytes) { 37 | throw std::runtime_error("Dropped packets or impl bugged\n"); 38 | } 39 | } 40 | 41 | // Here we have a simple test for adding and removing new forwarding IP 42 | // addresses. (UDPMultiForwarder) 43 | static void test_add_and_remove_forwarder() { 44 | SocketHelper::UDPMultiForwarder udpMultiForwarder{}; 45 | udpMultiForwarder.addForwarder("192.168.0.0", 5600); 46 | udpMultiForwarder.addForwarder("192.168.0.0", 5600); 47 | if (udpMultiForwarder.getForwarders().size() != 1) { 48 | throw std::runtime_error("Should not contain duplicates\n"); 49 | } 50 | udpMultiForwarder.addForwarder("192.168.0.1", 5600); 51 | if (udpMultiForwarder.getForwarders().size() != 2) { 52 | throw std::runtime_error("Should have 2 forwarders\n"); 53 | } 54 | udpMultiForwarder.removeForwarder("192.168.0.1", 5600); 55 | if (udpMultiForwarder.getForwarders().size() != 1) { 56 | throw std::runtime_error("Should have 1 forwarder\n"); 57 | } 58 | udpMultiForwarder.removeForwarder("192.168.0.0", 5600); 59 | if (!udpMultiForwarder.getForwarders().empty()) { 60 | throw std::runtime_error("Should have 0 forwarder\n"); 61 | } 62 | } 63 | 64 | int main(int argc, char *const *argv) { 65 | test_send_and_receive(); 66 | test_add_and_remove_forwarder(); 67 | return 0; 68 | } -------------------------------------------------------------------------------- /wifibroadcast/executables/test_dummy_link.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by consti10 on 07.01.24. 3 | // 4 | 5 | #include 6 | 7 | #include "../src/WBTxRx.h" 8 | #include "Helper.hpp" 9 | 10 | static std::vector>> 11 | pull_all_buffered_packets(DummyLink& dummyLink) { 12 | std::vector>> rx_packets; 13 | while (true) { 14 | auto packet = dummyLink.rx_radiotap(); 15 | if (!packet) break; 16 | rx_packets.push_back(packet); 17 | } 18 | return rx_packets; 19 | } 20 | 21 | static void test_dummy_socket_impl() { 22 | auto dummy_air = std::make_shared(true); 23 | auto dummy_gnd = std::make_shared(false); 24 | dummy_gnd->set_drop_mode(0); 25 | auto dummy_packets1 = GenericHelper::createRandomDataBuffers(20, 1024, 1024); 26 | auto dummy_packets2 = GenericHelper::createRandomDataBuffers(20, 1024, 1024); 27 | for (auto& packet : dummy_packets1) { 28 | dummy_air->tx_radiotap(packet.data(), packet.size()); 29 | } 30 | for (auto& packet : dummy_packets2) { 31 | dummy_gnd->tx_radiotap(packet.data(), packet.size()); 32 | } 33 | // wait until all packets are received (hopefully) 34 | std::this_thread::sleep_for(std::chrono::seconds(1)); 35 | auto rx_air = pull_all_buffered_packets(*dummy_air); 36 | auto rx_gnd = pull_all_buffered_packets(*dummy_gnd); 37 | GenericHelper::assertVectorsOfVectorsEqual(rx_gnd, dummy_packets1); 38 | GenericHelper::assertVectorsOfVectorsEqual(rx_air, dummy_packets2); 39 | std::cout << "Done test_dummy_socket_impl" << std::endl; 40 | } 41 | 42 | static std::shared_ptr make_txrx(bool air) { 43 | auto card = wifibroadcast::create_card_emulate(air); 44 | std::vector cards; 45 | cards.push_back(card); 46 | WBTxRx::Options options_txrx{}; 47 | options_txrx.log_all_received_validated_packets = true; 48 | options_txrx.rx_radiotap_debug_level = 3; 49 | options_txrx.advanced_debugging_rx = true; 50 | options_txrx.use_gnd_identifier = !air; 51 | options_txrx.log_all_received_packets = true; 52 | auto radiotap_header_holder_tx = std::make_shared(); 53 | std::shared_ptr txrx = 54 | std::make_shared(cards, options_txrx, radiotap_header_holder_tx); 55 | // 56 | return txrx; 57 | } 58 | 59 | static void test_wb_tx_rx_dummy() { 60 | auto tx_rx_air = make_txrx(true); 61 | auto tx_rx_gnd = make_txrx(false); 62 | tx_rx_air->start_receiving(); 63 | tx_rx_gnd->start_receiving(); 64 | 65 | auto dummy_packets1 = GenericHelper::createRandomDataBuffers(20, 1024, 1024); 66 | auto radiotap_header_holder_tx = std::make_shared(); 67 | 68 | for (auto& packet : dummy_packets1) { 69 | tx_rx_air->tx_inject_packet(5, packet.data(), packet.size(), 70 | radiotap_header_holder_tx->thread_safe_get(), 71 | true); 72 | std::this_thread::sleep_for(std::chrono::milliseconds(100)); 73 | } 74 | // Sleep a bit to make sure all queues are empty 75 | std::this_thread::sleep_for(std::chrono::seconds(1)); 76 | const auto rx_stats = tx_rx_gnd->get_rx_stats(); 77 | // RX should have received all the packets 78 | assert(rx_stats.count_p_valid == dummy_packets1.size()); 79 | tx_rx_air->stop_receiving(); 80 | tx_rx_gnd->stop_receiving(); 81 | } 82 | 83 | int main(int argc, char* const* argv) { 84 | test_dummy_socket_impl(); 85 | test_wb_tx_rx_dummy(); 86 | return 0; 87 | } 88 | -------------------------------------------------------------------------------- /wifibroadcast/executables/test_listen.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by consti10 on 07.10.23. 3 | // Uses WBTxRx to listen to all (openhd and non openhd) traffic 4 | // 5 | #include "../src/WBTxRx.h" 6 | #include "../src/wifibroadcast_spdlog.h" 7 | 8 | int main(int argc, char *const *argv) { 9 | std::string card = "wlxac9e17596103"; 10 | bool pcap_setdirection = true; 11 | int opt; 12 | while ((opt = getopt(argc, argv, "w:d")) != -1) { 13 | switch (opt) { 14 | case 'w': 15 | card = optarg; 16 | break; 17 | case 'd': 18 | pcap_setdirection = false; 19 | break; 20 | default: /* '?' */ 21 | show_usage: 22 | fprintf(stderr, "test_listen -w [wifi card to listen on] %s\n", 23 | argv[0]); 24 | exit(1); 25 | } 26 | } 27 | 28 | std::vector cards; 29 | wifibroadcast::WifiCard tmp_card{card, 1}; 30 | cards.push_back(tmp_card); 31 | WBTxRx::Options options_txrx{}; 32 | // options_txrx.pcap_rx_set_direction= false; 33 | options_txrx.pcap_rx_set_direction = pcap_setdirection; 34 | options_txrx.log_all_received_validated_packets = true; 35 | options_txrx.rx_radiotap_debug_level = 3; 36 | options_txrx.advanced_debugging_rx = true; 37 | auto radiotap_header_holder_tx = std::make_shared(); 38 | std::shared_ptr txrx = 39 | std::make_shared(cards, options_txrx, radiotap_header_holder_tx); 40 | 41 | txrx->start_receiving(); 42 | 43 | auto lastLog = std::chrono::steady_clock::now(); 44 | while (true) { 45 | std::this_thread::sleep_for(std::chrono::milliseconds(500)); 46 | // auto txStats=txrx->get_tx_stats(); 47 | auto rxStats = txrx->get_rx_stats(); 48 | auto rx_stats_card0 = txrx->get_rx_stats_for_card(0); 49 | auto rx_rf_stats_card0 = txrx->get_rx_rf_stats_for_card(0); 50 | // std::cout< 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "../src//HelperSources/Helper.hpp" 12 | #include "../src//HelperSources/TimeHelper.hpp" 13 | #include "../src/FunkyQueue.h" 14 | 15 | struct TestElement { 16 | std::chrono::steady_clock::time_point tp; 17 | std::shared_ptr> data; 18 | }; 19 | 20 | static std::shared_ptr make_test_element() { 21 | auto ret = std::make_shared(); 22 | ret->tp = std::chrono::steady_clock::now(); 23 | ret->data = GenericHelper::createRandomDataBuffer2(1000); 24 | return ret; 25 | } 26 | 27 | int main(int argc, char *const *argv) { 28 | using FunkyQueueImpl = FunkyQueue>; 29 | 30 | auto funky_queue = std::make_shared(2); 31 | 32 | auto packet1 = make_test_element(); 33 | auto packet2 = make_test_element(); 34 | auto packet3 = make_test_element(); 35 | auto packet4 = make_test_element(); 36 | 37 | assert(funky_queue->try_enqueue(packet1) == true); 38 | assert(funky_queue->try_enqueue(packet2) == true); 39 | assert(funky_queue->get_current_size() == 2); 40 | assert(funky_queue->try_enqueue(packet3) == false); 41 | assert(funky_queue->get_current_size() == 2); 42 | // Drops 2 currently enqueued packets 43 | assert(funky_queue->enqueue_or_clear_enqueue(packet3) == 2); 44 | assert(funky_queue->get_current_size() == 1); 45 | 46 | auto tmp = funky_queue->wait_dequeue_timed(std::chrono::milliseconds(100)); 47 | assert(tmp.has_value()); 48 | assert(funky_queue->get_current_size() == 0); 49 | 50 | const auto before_wait_no_data = std::chrono::steady_clock::now(); 51 | auto tmp2 = funky_queue->wait_dequeue_timed(std::chrono::milliseconds(100)); 52 | assert(!tmp2.has_value()); 53 | assert(std::chrono::steady_clock::now() - before_wait_no_data >= 54 | std::chrono::milliseconds(100)); 55 | assert(funky_queue->get_current_size() == 0); 56 | 57 | // Now queue is empty again 58 | int poll_thread_n_dequeued_packets = 0; 59 | std::atomic_bool poll_thread_run = true; 60 | auto poll_thread = std::make_unique( 61 | [&funky_queue, &poll_thread_n_dequeued_packets, &poll_thread_run] { 62 | const auto begin = std::chrono::steady_clock::now(); 63 | while (poll_thread_run) { 64 | auto tmp = 65 | funky_queue->wait_dequeue_timed(std::chrono::milliseconds(100)); 66 | if (tmp.has_value()) { 67 | auto &dequeued = *tmp.value(); 68 | const auto delta_enqueue_dequeue = 69 | std::chrono::steady_clock::now() - dequeued.tp; 70 | std::cout << "Got element, delay:" 71 | << MyTimeHelper::R(delta_enqueue_dequeue) << std::endl; 72 | poll_thread_n_dequeued_packets++; 73 | // As long as the OS is not overloaded / has issues scheduling tasks 74 | // ... 75 | assert(delta_enqueue_dequeue <= std::chrono::milliseconds(10)); 76 | } 77 | } 78 | }); 79 | for (int i = 0; i < 100; i++) { 80 | auto packet = make_test_element(); 81 | assert(funky_queue->try_enqueue(packet) == true); 82 | // During this time the poll_thread should dequeue the packet 83 | std::this_thread::sleep_for(std::chrono::milliseconds(33)); 84 | } 85 | std::this_thread::sleep_for(std::chrono::seconds(1)); 86 | poll_thread_run = false; 87 | poll_thread->join(); 88 | assert(funky_queue->get_current_size() == 0); 89 | 90 | return 0; 91 | } -------------------------------------------------------------------------------- /wifibroadcast/executables/test_txrx.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by consti10 on 27.06.23. 3 | // 4 | 5 | #include "../src/WBStreamRx.h" 6 | #include "../src/WBStreamTx.h" 7 | #include "../src/WBTxRx.h" 8 | #include "../src/wifibroadcast_spdlog.h" 9 | #include "RandomBufferPot.hpp" 10 | 11 | int main(int argc, char *const *argv) { 12 | std::string card = "wlxac9e17596103"; 13 | bool pcap_setdirection = true; 14 | int opt; 15 | while ((opt = getopt(argc, argv, "w:d")) != -1) { 16 | switch (opt) { 17 | case 'w': 18 | card = optarg; 19 | break; 20 | case 'd': 21 | pcap_setdirection = false; 22 | break; 23 | default: /* '?' */ 24 | show_usage: 25 | fprintf( 26 | stderr, 27 | "Local receiver: %s [-K rx_key] [-c client_addr] [-u " 28 | "udp_client_port] [-r radio_port] interface1 [interface2] ...\n", 29 | argv[0]); 30 | exit(1); 31 | } 32 | } 33 | 34 | std::vector cards; 35 | wifibroadcast::WifiCard tmp_card{card, 1}; 36 | cards.push_back(tmp_card); 37 | WBTxRx::Options options_txrx{}; 38 | // options_txrx.pcap_rx_set_direction= false; 39 | options_txrx.pcap_rx_set_direction = pcap_setdirection; 40 | options_txrx.log_all_received_validated_packets = true; 41 | auto radiotap_header_holder_tx = std::make_shared(); 42 | std::shared_ptr txrx = 43 | std::make_shared(cards, options_txrx, radiotap_header_holder_tx); 44 | 45 | const bool enable_fec = true; 46 | WBStreamTx::Options options_tx{}; 47 | options_tx.radio_port = 10; 48 | options_tx.enable_fec = enable_fec; 49 | auto radiotap_header_holder_rx = std::make_shared(); 50 | std::unique_ptr wb_tx = 51 | std::make_unique(txrx, options_tx, radiotap_header_holder_rx); 52 | 53 | WBStreamRx::Options options_rx{}; 54 | options_rx.radio_port = 10; 55 | options_rx.enable_fec = enable_fec; 56 | std::unique_ptr wb_rx = 57 | std::make_unique(txrx, options_rx); 58 | auto console = wifibroadcast::log::create_or_get("out_cb"); 59 | auto cb = [&console](const uint8_t *payload, const std::size_t payloadSize) { 60 | console->debug("Got data {}", payloadSize); 61 | }; 62 | wb_rx->set_callback(cb); 63 | 64 | txrx->start_receiving(); 65 | 66 | const auto randomBufferPot = std::make_unique(1000, 1024); 67 | 68 | auto lastLog = std::chrono::steady_clock::now(); 69 | while (true) { 70 | for (int i = 0; i < 100; i++) { 71 | auto dummy_packet = randomBufferPot->getBuffer(i); 72 | // txrx->tx_inject_packet(0,dummy_packet->data(),dummy_packet->size()); 73 | if (enable_fec) { 74 | wb_tx->try_enqueue_block({dummy_packet}, 10, 10); 75 | } else { 76 | wb_tx->try_enqueue_packet(dummy_packet); 77 | } 78 | std::this_thread::sleep_for(std::chrono::milliseconds(500)); 79 | const auto elapsed_since_last_log = 80 | std::chrono::steady_clock::now() - lastLog; 81 | if (elapsed_since_last_log > std::chrono::seconds(1)) { 82 | lastLog = std::chrono::steady_clock::now(); 83 | auto txStats = txrx->get_tx_stats(); 84 | auto rxStats = txrx->get_rx_stats(); 85 | auto rx_stats_card0 = txrx->get_rx_stats_for_card(0); 86 | std::cout << txStats << "\n"; 87 | std::cout << rxStats << "\n"; 88 | std::cout << rx_stats_card0 << std::endl; 89 | } 90 | } 91 | } 92 | } -------------------------------------------------------------------------------- /wifibroadcast/executables/udp_packet_drop_util.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by consti10 on 20.04.22. 3 | // This one just exists to test the compilation with cmake,nothing else. 4 | // 5 | #include 6 | 7 | #include "../src/HelperSources/EmulatedPacketDrop.hpp" 8 | #include "../src/HelperSources/SocketHelper.hpp" 9 | 10 | static std::unique_ptr udp_receiver; 11 | static std::unique_ptr udp_forwarder; 12 | static std::unique_ptr packet_drop_emulator; 13 | 14 | static void on_new_udp_data(const uint8_t *payload, 15 | const std::size_t payloadSize) { 16 | if (packet_drop_emulator->drop_packet()) { 17 | return; 18 | } 19 | udp_forwarder->forwardPacketViaUDP(payload, payloadSize); 20 | } 21 | 22 | int main(int argc, char *const *argv) { 23 | udp_forwarder = 24 | std::make_unique("127.0.0.1", 5600); 25 | packet_drop_emulator = std::make_unique(0); 26 | 27 | udp_receiver = std::make_unique( 28 | "127.0.0.1", 5599, 29 | [](const uint8_t *payload, const std::size_t payloadSize) { 30 | on_new_udp_data(payload, payloadSize); 31 | }); 32 | udp_receiver->runInBackground(); 33 | 34 | while (true) { 35 | printf("Write new drop percentage and press enter to change\n"); 36 | std::string input; 37 | std::cin >> input; 38 | std::cout << "Got:[" << input << "]\n"; 39 | const int perc = std::stoi(input); 40 | if (perc >= 0 && perc <= 100) { 41 | packet_drop_emulator->set_new_percentage(perc); 42 | std::cout << "Changed drop to " << perc << "%\n"; 43 | } 44 | } 45 | udp_receiver->stopBackground(); 46 | return 0; 47 | } -------------------------------------------------------------------------------- /wifibroadcast/executables/wfb_keygen.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (C) 2017, 2018 Vasily Evseenko 3 | // 2020 Constantin Geier 4 | 5 | /* 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; version 3. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | 20 | #include 21 | 22 | #include 23 | #include 24 | 25 | #include "../src/encryption/Encryption.h" 26 | #include "../src/encryption/EncryptionFsUtils.h" 27 | 28 | /** 29 | * Generates a new tx rx keypair and saves it to file for later use. 30 | */ 31 | int main(int argc, char *const *argv) { 32 | int opt; 33 | std::optional bind_phrase = std::nullopt; 34 | while ((opt = getopt(argc, argv, "b:")) != -1) { 35 | switch (opt) { 36 | case 'b': { 37 | bind_phrase = std::string(optarg); 38 | } break; 39 | default: /* '?' */ 40 | show_usage: 41 | fprintf( 42 | stderr, 43 | "wfb-keygen [-b bind_phrase,deterministic], if no bind phrase is " 44 | "specified, random keys are generated (non-deterministic) %s\n", 45 | argv[0]); 46 | exit(1); 47 | } 48 | } 49 | wb::KeyPairTxRx keyPairTxRx{}; 50 | if (bind_phrase.has_value()) { 51 | std::cout << "Generating txrx keypair using bind phrase [" 52 | << bind_phrase.value() << "]" << std::endl; 53 | keyPairTxRx = wb::generate_keypair_from_bind_phrase(bind_phrase.value()); 54 | } else { 55 | std::cout << "Generating random txrx keypair" << std::endl; 56 | keyPairTxRx = wb::generate_keypair_random(); 57 | } 58 | // auto keypair=wb::generate_keypair_from_bind_phrase("openhd"); 59 | auto res = wb::write_keypair_to_file(keyPairTxRx, "txrx.key"); 60 | if (res) { 61 | std::cout << "Wrote keypair to file" << std::endl; 62 | return 0; 63 | } else { 64 | std::cout << "Cannot write keypair to file" << std::endl; 65 | return -1; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /wifibroadcast/lib/Readme.txt: -------------------------------------------------------------------------------- 1 | Here I collect code that once came from 'some github repository' and should not be modified. 2 | Only maybe 'Updated' when these repositories receive updates 3 | Also, some of this code is written in 'c' not 'c++' 4 | 5 | 30.Jan 2021: 6 | The sources from "zfec" are not used anymore (https://github.com/tahoe-lafs/zfec/tree/master/zfec) 7 | The fec stuff was copied from 8 | https://github.com/OpenHD/Open.HD/blob/8f7be98a3b7c97f325ae655256c81bea09199834/wifibroadcast-base/fec.c 9 | and the syntax was improved slightly (make const when dealing with buffers data is not written to) 10 | The "radiotap stuff" comes from https://github.com/radiotap/radiotap-library 11 | 12 | 30.Jan 2021: 13 | Optimized the FEC encode/decode using NEON and SSSE3 14 | 15 | -------------------------------------------------------------------------------- /wifibroadcast/lib/fec/gf_optimized/Readme.md: -------------------------------------------------------------------------------- 1 | All this code was taken from https://github.com/moepinet/libmoepgf \ 2 | Since we only need gf256 for this FEC implementation (and also only need the "shuffle" implementations when optimized, 3 | since they are generally the fastest), I decided to just copy the right methods from the repository above in a 4 | header-only style. NOTE: the optimized methods generally require a multiple of 8 or 16 bytes. The pattern when optimized 5 | is generally the same (mul,addmul): 6 | 7 | 1) use the fastest implementation for as many bytes as there are multiples of (8/16/32 depending on arch) 8 | 2) use the slow (table) implementation for the rest of the bytes 9 | 3) there is no optimized method available, flat table is used as a fallback 10 | 11 | Also note: I only bothered to add NEON and SSSE3 shuffle optimized methods. In the rare case of NEON not being available 12 | on ARM,no optimization exists (one could try a left and right part table lookup without NEON) and the performance is 13 | therefore really bad. In the case of X86, AVX2 could provide a performance benefit compared to SSSE3, but I did not 14 | bother adding AVX2 support, since SSSE3 is already fast enough for our use case. -------------------------------------------------------------------------------- /wifibroadcast/lib/fec/gf_optimized/alignment_check.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by consti10 on 08.01.22. 3 | // 4 | 5 | #ifndef WIFIBROADCAST_ALIGNMENT_CHECK_H 6 | #define WIFIBROADCAST_ALIGNMENT_CHECK_H 7 | 8 | // In the beginning I had some problems with the optimized methods regarding the data alignment 9 | // These issues are fixed now, though. The file is therefore obsolete. 10 | 11 | #include 12 | 13 | static inline bool is_aligned(const void *pointer, size_t byte_count) { return (uintptr_t) pointer % byte_count == 0; } 14 | 15 | static inline bool are_aligned(const void *pointer1, const void *pointer2, size_t byte_count) { 16 | return is_aligned(pointer1, byte_count) && is_aligned(pointer2, byte_count); 17 | } 18 | 19 | // Not guaranteed that a value exists that has the proper alignment for both input arrays - 20 | // well I was able to fix it otherwise 21 | static inline int find_alignment(const uint8_t *pointer1, const uint8_t *pointer2, size_t byte_count) { 22 | int ret = 0; 23 | while (!(is_aligned(&pointer1[ret], byte_count) && is_aligned(&pointer2[ret], byte_count))) { 24 | ret += 8; 25 | } 26 | if (ret != 0) { 27 | std::cout << "Alignment okay after " << ret << "bytes\n"; 28 | } 29 | return ret; 30 | } 31 | 32 | #endif //WIFIBROADCAST_ALIGNMENT_CHECK_H 33 | -------------------------------------------------------------------------------- /wifibroadcast/lib/fec/gf_optimized/gf256_avx2.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by consti10 on 04.01.22. 3 | // 4 | 5 | #ifndef LIBMOEPGF_GF256_AVX2_H 6 | #define LIBMOEPGF_GF256_AVX2_H 7 | 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | #include "gf256tables285.h" 14 | 15 | // fastest option for x86 if AVX2 is supported 16 | // NOTE - I ended up not using the AVX2 code for the following reasons: 17 | // 1) since working on 32 bytes, the alignment issue is even more apparent 18 | // 2) ssse3 is already "fast enough" and supported on pretty much any platform 19 | // (other than AVX2) If you want to, add and test the code here ;) 20 | 21 | static const uint8_t tl[MOEPGF256_SIZE][16] = MOEPGF256_SHUFFLE_LOW_TABLE; 22 | static const uint8_t th[MOEPGF256_SIZE][16] = MOEPGF256_SHUFFLE_HIGH_TABLE; 23 | 24 | static inline bool is_aligned(const void *pointer, size_t byte_count) { 25 | return (uintptr_t)pointer % byte_count == 0; 26 | } 27 | 28 | void xorr_avx2(uint8_t *region1, const uint8_t *region2, size_t length) { 29 | assert(length % 32 == 0); 30 | std::cout << "xorr_avx2\n"; 31 | assert(is_aligned(region1, 32)); 32 | assert(is_aligned(region2, 32)); 33 | // std::cout<<""<<(int)region1[length]<<":"<<(int)region2[length]<<"\n"; 34 | 35 | uint8_t *end; 36 | __m256i in, out; 37 | 38 | for (end = region1 + length; region1 < end; region1 += 32, region2 += 32) { 39 | std::cout << "XXx\n"; 40 | /*in = _mm256_load_si256((const __m256i*)region2); 41 | out = _mm256_load_si256((const __m256i*)region1); 42 | out = _mm256_xor_si256(in, out); 43 | _mm256_store_si256((__m256i *)region1, out);*/ 44 | in = _mm256_loadu_si256((const __m256i *)region2); 45 | out = _mm256_loadu_si256((const __m256i *)region1); 46 | out = _mm256_xor_si256(in, out); 47 | _mm256_storeu_si256((__m256i *)region1, out); 48 | std::cout << "YY\n"; 49 | } 50 | } 51 | 52 | void maddrc256_shuffle_avx2(uint8_t *region1, const uint8_t *region2, 53 | uint8_t constant, size_t length) { 54 | assert(length % 32 == 0); 55 | // std::cout<<"maddrc256_shuffle_avx2:"< 10 | 11 | // Slower, but compiles on any hardware 12 | 13 | static const uint8_t mult[MOEPGF256_SIZE][MOEPGF256_SIZE] = MOEPGF256_MUL_TABLE; 14 | 15 | static void 16 | xorr_scalar(uint8_t *region1, const uint8_t *region2, size_t length) { 17 | for (; length; region1++, region2++, length--) 18 | *region1 ^= *region2; 19 | } 20 | 21 | static void 22 | maddrc256_flat_table(uint8_t *region1, const uint8_t *region2, 23 | uint8_t constant, size_t length) { 24 | if (constant == 0) 25 | return; 26 | 27 | if (constant == 1) { 28 | xorr_scalar(region1, region2, length); 29 | return; 30 | } 31 | 32 | for (; length; region1++, region2++, length--) { 33 | *region1 ^= mult[constant][*region2]; 34 | } 35 | 36 | } 37 | 38 | static void 39 | mulrc256_flat_table(uint8_t *region1, const uint8_t *region2, 40 | uint8_t constant, size_t length) { 41 | if (constant == 0) 42 | memset(region1, 0, length); 43 | 44 | if (constant == 1) { 45 | memcpy(region1, region2, length); 46 | return; 47 | } 48 | 49 | for (; length; region1++, region2++, length--) { 50 | *region1 = mult[constant][*region2]; 51 | } 52 | } 53 | 54 | #endif //LIBMOEPGF_GF256_FLAT_TABLE_H 55 | -------------------------------------------------------------------------------- /wifibroadcast/lib/fec/gf_optimized/gf256_neon.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by consti10 on 02.01.22. 3 | // 4 | #ifndef LIBMOEPGF_GF256_NEON_H 5 | #define LIBMOEPGF_GF256_NEON_H 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | #include "gf256tables285.h" 13 | 14 | // Fastest if NEON is supported 15 | // Regrading alignment: 16 | // https://developer.arm.com/documentation/ddi0344/f/Cihejdic I think neon by 17 | // default doesn't care about alignment, only if the alignment is explicitly 18 | // specified it needs to match 19 | 20 | static const uint8_t tl[MOEPGF256_SIZE][16] = MOEPGF256_SHUFFLE_LOW_TABLE; 21 | static const uint8_t th[MOEPGF256_SIZE][16] = MOEPGF256_SHUFFLE_HIGH_TABLE; 22 | 23 | void xorr_neon_64(uint8_t *region1, const uint8_t *region2, size_t length) { 24 | // std::cout<<"Xx neon"< 56 | #include 57 | 58 | // computes dst[] = c * src[] 59 | // where '+', '*' are gf256 operations 60 | static void gf256_mul_optimized(uint8_t *dst, const uint8_t *src, gf c, const int sz) { 61 | #ifdef FEC_GF256_USE_X86_SSSE3 62 | const int sizeSlow = sz % 16; 63 | const int sizeFast = sz - sizeSlow; 64 | if(sizeFast>0){ 65 | mulrc256_shuffle_ssse3(dst,src,c,sizeFast); 66 | } 67 | if(sizeSlow>0){ 68 | mulrc256_flat_table(&dst[sizeFast],&src[sizeFast],c,sizeSlow); 69 | } 70 | #elif defined(FEC_GF256_USE_ARM_NEON) 71 | const int sizeSlow = sz % 8; 72 | const int sizeFast = sz - sizeSlow; 73 | if(sizeFast>0){ 74 | mulrc256_shuffle_neon_64(dst,src,c,sizeFast); 75 | } 76 | if(sizeSlow>0){ 77 | mulrc256_flat_table(&dst[sizeFast],&src[sizeFast],c,sizeSlow); 78 | } 79 | #else 80 | mulrc256_flat_table(dst, src, c, sz); 81 | #endif 82 | } 83 | 84 | // computes dst[] = dst[] + c * src[] 85 | // where '+', '*' are gf256 operations 86 | static void gf256_madd_optimized(uint8_t *dst, const uint8_t *src, gf c, const int sz) { 87 | #ifdef FEC_GF256_USE_X86_SSSE3 88 | const int sizeSlow = sz % 16; 89 | const int sizeFast = sz - sizeSlow; 90 | if(sizeFast>0){ 91 | maddrc256_shuffle_ssse3(dst,src,c,sizeFast); 92 | } 93 | if(sizeSlow>0){ 94 | maddrc256_flat_table(&dst[sizeFast],&src[sizeFast],c,sizeSlow); 95 | } 96 | //maddrc256_flat_table(dst,src,c,sz); 97 | #elif defined(FEC_GF256_USE_ARM_NEON) 98 | const int sizeSlow = sz % 8; 99 | const int sizeFast = sz - sizeSlow; 100 | if(sizeFast>0){ 101 | maddrc256_shuffle_neon_64(dst,src,c,sizeFast); 102 | } 103 | if(sizeSlow>0){ 104 | maddrc256_flat_table(&dst[sizeFast],&src[sizeFast],c,sizeSlow); 105 | } 106 | #else 107 | maddrc256_flat_table(dst, src, c, sz); 108 | #endif 109 | } 110 | 111 | static const uint8_t inverses[MOEPGF256_SIZE] = MOEPGF256_INV_TABLE; 112 | 113 | // for the inverse of a number we don't have a highly optimized method 114 | // since it is never done on big chunks of memory anyways 115 | static uint8_t gf256_inverse(uint8_t value) { 116 | return inverses[value]; 117 | } 118 | 119 | // and sometimes the FEC code needs to just multiply two uint8_t values (not a memory region) 120 | static uint8_t gf256_mul(uint8_t x, uint8_t y) { 121 | uint8_t ret; 122 | mulrc256_flat_table(&ret, &x, y, 1); 123 | return ret; 124 | } 125 | 126 | static void gf256_print_optimization_method() { 127 | #ifdef FEC_GF256_USE_X86_SSSE3 128 | std::cout<<"FEC Using X86_SSSE3 optimization\n"; 129 | #elif defined(FEC_GF256_USE_ARM_NEON) 130 | std::cout<<"FEC Using ARM_NEON optimization\n"; 131 | #else 132 | std::cout << "WARNING FEC No optimization, using flat_table as fallback\n"; 133 | #endif 134 | } 135 | 136 | #endif //WIFIBROADCAST_GF256_SIMPLE_INCLUDE_H 137 | -------------------------------------------------------------------------------- /wifibroadcast/lib/fec/gf_optimized/gf256_ssse3.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by consti10 on 08.01.22. 3 | // 4 | 5 | #ifndef WIFIBROADCAST_GF256_SSE3_H 6 | #define WIFIBROADCAST_GF256_SSE3_H 7 | 8 | #include 9 | #include 10 | 11 | #include "gf256tables285.h" 12 | 13 | // also fast on x86 - and supported on many more platforms than AVX2. 14 | // AVX2 'could' beat ssse3, but for my personal use case this difference didn't 15 | // justify making the optimization even more complex Regarding alignment: I 16 | // modified the code to use "u" (unaligned) instructions everyhwere, so 17 | // alignment doesn't matter anymore It used to be different in the original 18 | // impl. 19 | 20 | static const uint8_t tl[MOEPGF256_SIZE][16] = MOEPGF256_SHUFFLE_LOW_TABLE; 21 | static const uint8_t th[MOEPGF256_SIZE][16] = MOEPGF256_SHUFFLE_HIGH_TABLE; 22 | 23 | void xorr_sse2(uint8_t *region1, const uint8_t *region2, size_t length) { 24 | assert(length % 16 == 0); 25 | uint8_t *end; 26 | __m128i in, out; 27 | 28 | for (end = region1 + length; region1 < end; region1 += 16, region2 += 16) { 29 | in = _mm_loadu_si128((const __m128i *)region2); 30 | out = _mm_loadu_si128((const __m128i *)region1); 31 | out = _mm_xor_si128(in, out); 32 | _mm_storeu_si128((__m128i *)region1, out); 33 | } 34 | } 35 | 36 | void maddrc256_shuffle_ssse3(uint8_t *region1, const uint8_t *region2, 37 | uint8_t constant, size_t length) { 38 | assert(length % 16 == 0); 39 | uint8_t *end; 40 | __m128i t1, t2, m1, m2, in1, in2, out, l, h; 41 | 42 | if (constant == 0) return; 43 | 44 | if (constant == 1) { 45 | xorr_sse2(region1, region2, length); 46 | return; 47 | } 48 | 49 | t1 = _mm_loadu_si128((const __m128i *)tl[constant]); 50 | t2 = _mm_loadu_si128((const __m128i *)th[constant]); 51 | m1 = _mm_set1_epi8(0x0f); 52 | m2 = _mm_set1_epi8(0xf0); 53 | 54 | for (end = region1 + length; region1 < end; region1 += 16, region2 += 16) { 55 | in2 = _mm_loadu_si128((const __m128i *)region2); 56 | in1 = _mm_loadu_si128((const __m128i *)region1); 57 | l = _mm_and_si128(in2, m1); 58 | l = _mm_shuffle_epi8(t1, l); 59 | h = _mm_and_si128(in2, m2); 60 | h = _mm_srli_epi64(h, 4); 61 | h = _mm_shuffle_epi8(t2, h); 62 | out = _mm_xor_si128(h, l); 63 | out = _mm_xor_si128(out, in1); 64 | _mm_storeu_si128((__m128i *)region1, out); 65 | } 66 | } 67 | 68 | void mulrc256_shuffle_ssse3(uint8_t *region1, const uint8_t *region2, 69 | uint8_t constant, size_t length) { 70 | assert(length % 16 == 0); 71 | uint8_t *end; 72 | __m128i t1, t2, m1, m2, in, out, l, h; 73 | 74 | if (constant == 0) { 75 | memset(region1, 0, length); 76 | return; 77 | } 78 | 79 | if (constant == 1) { 80 | memcpy(region1, region2, length); 81 | return; 82 | } 83 | 84 | t1 = _mm_loadu_si128((const __m128i *)tl[constant]); 85 | t2 = _mm_loadu_si128((const __m128i *)th[constant]); 86 | m1 = _mm_set1_epi8(0x0f); 87 | m2 = _mm_set1_epi8(0xf0); 88 | 89 | for (end = region1 + length; region1 < end; region1 += 16, region2 += 16) { 90 | in = _mm_loadu_si128((const __m128i *)region2); 91 | l = _mm_and_si128(in, m1); 92 | l = _mm_shuffle_epi8(t1, l); 93 | h = _mm_and_si128(in, m2); 94 | h = _mm_srli_epi64(h, 4); 95 | h = _mm_shuffle_epi8(t2, h); 96 | out = _mm_xor_si128(h, l); 97 | _mm_storeu_si128((__m128i *)region1, out); 98 | } 99 | } 100 | 101 | #endif // WIFIBROADCAST_GF256_SSE3_H 102 | -------------------------------------------------------------------------------- /wifibroadcast/lib/fec/gf_simple/gf_simple.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by consti10 on 02.01.22. 3 | // 4 | 5 | #ifndef WIFIBROADCAST_GAL_MATH_REFERENCE_H 6 | #define WIFIBROADCAST_GAL_MATH_REFERENCE_H 7 | 8 | // from https://gist.github.com/meagtan/dc1adff8d84bb895891d8fd027ec9d8c 9 | // This code exists to validate the (optimized) gf256 math operations at run 10 | // time 11 | 12 | typedef unsigned char gal8; /* Galois field of order 2^8 */ 13 | 14 | const gal8 min_poly = 15 | 0b11101, /* Minimal polynomial x^8 + x^4 + x^3 + x^2 + 1 */ 16 | generator = 0b10; /* Generator of Galois field */ 17 | 18 | /* Add two elements of GF(2^8) */ 19 | gal8 gal_add(gal8 a, gal8 b) { return a ^ b; } 20 | 21 | /* Multiply two elements of GF(2^8) */ 22 | gal8 gal_mul(gal8 a, gal8 b) { 23 | gal8 res = 0; 24 | for (; b; b >>= 1) { 25 | if (b & 1) res ^= a; 26 | if (a & 0x80) 27 | a = (a << 1) ^ min_poly; 28 | else 29 | a <<= 1; 30 | } 31 | return res; 32 | } 33 | 34 | // Consti10 35 | // multiply and add 3 elements of GF(2^8) 36 | // return x + a*b 37 | gal8 gal_madd(gal8 x, gal8 a, gal8 b) { return gal_add(x, gal_mul(a, b)); } 38 | 39 | // same as above, but for memory regions ( to test optimized versions) 40 | static void gal_mul_region(gf *dst, const gf *src, gf c, const int sz) { 41 | for (int i = 0; i < sz; i++) { 42 | dst[i] = gal_mul(src[i], c); 43 | } 44 | } 45 | static void gal_madd_region(gf *dst, const gf *src, gf c, const int sz) { 46 | for (int i = 0; i < sz; i++) { 47 | dst[i] = gal_madd(dst[i], src[i], c); 48 | } 49 | } 50 | 51 | #endif // WIFIBROADCAST_GAL_MATH_REFERENCE_H 52 | -------------------------------------------------------------------------------- /wifibroadcast/lib/radiotap/platform.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #if defined(__APPLE__) 4 | #include 5 | #else 6 | #include 7 | #endif 8 | 9 | #define le16_to_cpu le16toh 10 | #define le32_to_cpu le32toh 11 | #define get_unaligned(p) \ 12 | ({ \ 13 | struct packed_dummy_struct { \ 14 | typeof(*(p)) __val; \ 15 | } __attribute__((packed)) *__ptr = (void *) (p); \ 16 | \ 17 | __ptr->__val; \ 18 | }) 19 | #define get_unaligned_le16(p) le16_to_cpu(get_unaligned((uint16_t *)(p))) 20 | #define get_unaligned_le32(p) le32_to_cpu(get_unaligned((uint32_t *)(p))) 21 | -------------------------------------------------------------------------------- /wifibroadcast/lib/radiotap/radiotap_iter.h: -------------------------------------------------------------------------------- 1 | #ifndef __RADIOTAP_ITER_H 2 | #define __RADIOTAP_ITER_H 3 | 4 | #include 5 | #include "radiotap.h" 6 | 7 | /* Radiotap header iteration 8 | * implemented in radiotap.c 9 | */ 10 | 11 | struct radiotap_override { 12 | uint8_t field; 13 | uint8_t align: 4, size: 4; 14 | }; 15 | 16 | struct radiotap_align_size { 17 | uint8_t align: 4, size: 4; 18 | }; 19 | 20 | struct ieee80211_radiotap_namespace { 21 | const struct radiotap_align_size *align_size; 22 | int n_bits; 23 | uint32_t oui; 24 | uint8_t subns; 25 | }; 26 | 27 | struct ieee80211_radiotap_vendor_namespaces { 28 | const struct ieee80211_radiotap_namespace *ns; 29 | int n_ns; 30 | }; 31 | 32 | /** 33 | * struct ieee80211_radiotap_iterator - tracks walk thru present radiotap args 34 | * @this_arg_index: index of current arg, valid after each successful call 35 | * to ieee80211_radiotap_iterator_next() 36 | * @this_arg: pointer to current radiotap arg; it is valid after each 37 | * call to ieee80211_radiotap_iterator_next() but also after 38 | * ieee80211_radiotap_iterator_init() where it will point to 39 | * the beginning of the actual data portion 40 | * @this_arg_size: length of the current arg, for convenience 41 | * @current_namespace: pointer to the current namespace definition 42 | * (or internally %NULL if the current namespace is unknown) 43 | * @is_radiotap_ns: indicates whether the current namespace is the default 44 | * radiotap namespace or not 45 | * 46 | * @overrides: override standard radiotap fields 47 | * @n_overrides: number of overrides 48 | * 49 | * @_rtheader: pointer to the radiotap header we are walking through 50 | * @_max_length: length of radiotap header in cpu byte ordering 51 | * @_arg_index: next argument index 52 | * @_arg: next argument pointer 53 | * @_next_bitmap: internal pointer to next present u32 54 | * @_bitmap_shifter: internal shifter for curr u32 bitmap, b0 set == arg present 55 | * @_vns: vendor namespace definitions 56 | * @_next_ns_data: beginning of the next namespace's data 57 | * @_reset_on_ext: internal; resetNewSession the arg index to 0 when going to the 58 | * next bitmap word 59 | * 60 | * Describes the radiotap parser state. Fields prefixed with an underscore 61 | * must not be used by users of the parser, only by the parser internally. 62 | */ 63 | 64 | struct ieee80211_radiotap_iterator { 65 | struct ieee80211_radiotap_header *_rtheader; 66 | const struct ieee80211_radiotap_vendor_namespaces *_vns; 67 | const struct ieee80211_radiotap_namespace *current_namespace; 68 | 69 | unsigned char *_arg, *_next_ns_data; 70 | uint32_t *_next_bitmap; 71 | 72 | unsigned char *this_arg; 73 | const struct radiotap_override *overrides; /* Only for RADIOTAP_SUPPORT_OVERRIDES */ 74 | int n_overrides; /* Only for RADIOTAP_SUPPORT_OVERRIDES */ 75 | int this_arg_index; 76 | int this_arg_size; 77 | 78 | int is_radiotap_ns; 79 | 80 | int _max_length; 81 | int _arg_index; 82 | uint32_t _bitmap_shifter; 83 | int _reset_on_ext; 84 | }; 85 | 86 | #ifdef __cplusplus 87 | #define CALLING_CONVENTION "C" 88 | #else 89 | #define CALLING_CONVENTION 90 | #endif 91 | 92 | extern CALLING_CONVENTION int ieee80211_radiotap_iterator_init( 93 | struct ieee80211_radiotap_iterator *iterator, 94 | struct ieee80211_radiotap_header *radiotap_header, 95 | int max_length, const struct ieee80211_radiotap_vendor_namespaces *vns); 96 | 97 | extern CALLING_CONVENTION int ieee80211_radiotap_iterator_next( 98 | struct ieee80211_radiotap_iterator *iterator); 99 | 100 | #endif /* __RADIOTAP_ITER_H */ 101 | -------------------------------------------------------------------------------- /wifibroadcast/src/FunkyQueue.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by consti10 on 02.02.24. 3 | // 4 | 5 | #include "FunkyQueue.h" 6 | -------------------------------------------------------------------------------- /wifibroadcast/src/FunkyQueue.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by consti10 on 02.02.24. 3 | // 4 | 5 | #ifndef WIFIBROADCAST_FUNKYQUEUE_H 6 | #define WIFIBROADCAST_FUNKYQUEUE_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | /** 16 | * Thread-safe queue for the openhd use case where there is one producer thread 17 | * (encoder) and one consumer thread (link). Funky because the operations are a 18 | * bit funky (but make sense in this use case). 19 | */ 20 | template 21 | class FunkyQueue { 22 | public: 23 | explicit FunkyQueue(int capacity) : m_capacity(capacity){}; 24 | // Enqueues a new element. Return true on success, false otherwise 25 | bool try_enqueue(T element) { 26 | std::unique_lock lk(mtx); 27 | if (queue.size() >= m_capacity) { 28 | return false; 29 | } 30 | queue.push(element); 31 | lk.unlock(); 32 | cv.notify_one(); 33 | return true; 34 | } 35 | // If there is enough space on the queue, enqueue the given element and return 36 | // 0; Otherwise, remove all elements currently in the queue, then enqueue the 37 | // given element, and return the n of removed elements 38 | int enqueue_or_clear_enqueue(T element) { 39 | std::unique_lock lk(mtx); 40 | if (queue.size() >= m_capacity) { 41 | // Not enough space 42 | const int count_removed = queue.size(); 43 | while (!queue.empty()) queue.pop(); 44 | queue.push(element); 45 | lk.unlock(); 46 | cv.notify_one(); 47 | return count_removed; 48 | } 49 | // enough space 50 | queue.push(element); 51 | lk.unlock(); 52 | cv.notify_one(); 53 | return 0; 54 | } 55 | // Wait up to timeout until element is available. 56 | template 57 | std::optional wait_dequeue_timed( 58 | std::chrono::duration const& timeout) { 59 | std::unique_lock ul(mtx); 60 | if (!queue.empty()) { 61 | auto tmp = queue.front(); 62 | queue.pop(); 63 | return tmp; 64 | } 65 | const auto res = 66 | cv.wait_for(ul, timeout, [this]() { return !queue.empty(); }); 67 | if (!res) { 68 | // Timeout 69 | return std::nullopt; 70 | } 71 | assert(!queue.empty()); 72 | auto tmp = queue.front(); 73 | queue.pop(); 74 | return tmp; 75 | } 76 | int get_current_size() { 77 | std::unique_lock ul(mtx); 78 | return queue.size(); 79 | } 80 | 81 | private: 82 | const int m_capacity; 83 | std::queue queue; 84 | std::mutex mtx; 85 | std::condition_variable cv; 86 | }; 87 | 88 | #endif // WIFIBROADCAST_FUNKYQUEUE_H 89 | -------------------------------------------------------------------------------- /wifibroadcast/src/HelperSources/BlockSizeHelper.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by consti10 on 07.12.22. 3 | // 4 | 5 | #ifndef WIFIBROADCAST_SRC_HELPERSOURCES_BLOCKSIZEHELPER_HPP_ 6 | #define WIFIBROADCAST_SRC_HELPERSOURCES_BLOCKSIZEHELPER_HPP_ 7 | 8 | #include 9 | #include 10 | 11 | namespace blocksize { 12 | 13 | // From 14 | // https://stackoverflow.com/questions/2745074/fast-ceiling-of-an-integer-division-in-c-c 15 | static int div_ceil(int numerator, int denominator) { 16 | std::div_t res = std::div(numerator, denominator); 17 | return res.rem ? (res.quot + 1) : res.quot; 18 | } 19 | 20 | // If a frame has more fragments than the max block size on this platform 21 | // (Which usually depends on the compute power of the platform we are running 22 | // on, since FEC blocks become exponentially increasing expensive the bigger 23 | // they are) We need to split the frame into more than one block. Usually, this 24 | // needs to be done only for key frames (which are much bigger than other 25 | // frame(s) ), but depends on the platform compute and encoder bitrate, fps 26 | static int calc_min_n_of_blocks(int fragments_in_this_frame, 27 | int max_block_size) { 28 | return std::ceil(static_cast(fragments_in_this_frame) / 29 | static_cast(max_block_size)); 30 | } 31 | 32 | // Algorithm: 33 | // Given some amount of balls, fill the minimum amount of buckets as equally 34 | // distributed as possible with balls such that each bucket has not more than 35 | // max_block_size balls 36 | static std::vector fill_buckets_evenly(int count, int max_size_of_bucket) { 37 | if (count <= max_size_of_bucket) { 38 | return {count}; 39 | } 40 | int consumed = 0; 41 | std::vector ret; 42 | while (consumed < count) { 43 | int remaining = count - consumed; 44 | const int fill = div_ceil(remaining, max_size_of_bucket); 45 | ret.push_back(fill); 46 | consumed += fill; 47 | } 48 | return ret; 49 | } 50 | 51 | static std::vector calculate_best_fit_block_sizes( 52 | int fragments_in_this_frame, int max_block_size) { 53 | if (fragments_in_this_frame <= max_block_size) { 54 | // We can do this whole frame in one FEC block 55 | return {static_cast(fragments_in_this_frame)}; 56 | } 57 | // Algorithm: 58 | // Given some amount of balls, fill the minimum amount of buckets as equally 59 | // distributed as possible with balls such that each bucket has not more than 60 | // max_block_size balls We need at least this many buckets (blocks) 61 | const int min_n_of_blocks = 62 | calc_min_n_of_blocks(fragments_in_this_frame, max_block_size); 63 | std::vector ret; 64 | ret.resize(min_n_of_blocks); 65 | // Fill the buckets (blocks) with fragments, one after another, until we run 66 | // out of balls (fragments) 67 | int remaining = fragments_in_this_frame; 68 | int index = 0; 69 | while (remaining > 0) { 70 | ret[index]++; 71 | remaining--; 72 | index++; 73 | index = index % min_n_of_blocks; 74 | } 75 | return ret; 76 | } 77 | 78 | static std::vector>>> 79 | split_frame_if_needed( 80 | const std::vector>>& frame_fragments, 81 | int max_block_size) { 82 | auto split = 83 | calculate_best_fit_block_sizes(frame_fragments.size(), max_block_size); 84 | if (split.size() == 1) { 85 | return {frame_fragments}; 86 | } 87 | std::vector>>> ret; 88 | ret.resize(split.size()); 89 | int n_used_fragments = 0; 90 | for (int i = 0; i < split.size(); i++) { 91 | for (int j = 0; j < split[i]; j++) { 92 | ret[i].push_back(frame_fragments[n_used_fragments]); 93 | n_used_fragments++; 94 | } 95 | } 96 | return ret; 97 | } 98 | 99 | // Given the size of a frame and the max n of primary fragments we can do on the 100 | // given platform calculate how we should distribute the data (into one or more 101 | // fec blocks, and how many fragments each fec block shall have 102 | 103 | static int min_num_sub_blocks(int frame_size, int max_block_size, int MTU) { 104 | const int max_data_size = max_block_size * MTU; 105 | return div_ceil(frame_size, max_data_size); 106 | } 107 | 108 | } // namespace blocksize 109 | #endif // WIFIBROADCAST_SRC_HELPERSOURCES_BLOCKSIZEHELPER_HPP_ 110 | -------------------------------------------------------------------------------- /wifibroadcast/src/HelperSources/DummyStreamGenerator.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by consti10 on 25.07.23. 3 | // 4 | 5 | #ifndef WIFIBROADCAST_DUMMYSTREAMGENERATOR_HPP 6 | #define WIFIBROADCAST_DUMMYSTREAMGENERATOR_HPP 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "RandomBufferPot.hpp" 14 | #include "SchedulingHelper.hpp" 15 | 16 | /** 17 | * Generates as close as possible a stream of data packets with a target packets 18 | * per second packet rate. 19 | */ 20 | class DummyStreamGenerator { 21 | public: 22 | typedef std::function 23 | OUTPUT_DATA_CALLBACK; 24 | 25 | DummyStreamGenerator(OUTPUT_DATA_CALLBACK cb, int packet_size) 26 | : m_cb(std::move(cb)), m_packet_size(packet_size) { 27 | m_random_buffer_pot = 28 | std::make_unique(1000, m_packet_size); 29 | }; 30 | ~DummyStreamGenerator() { stop(); } 31 | 32 | void set_target_pps(int pps) { m_target_pps = pps; } 33 | 34 | void start() { 35 | m_terminate = false; 36 | m_producer_thread = 37 | std::make_unique([this]() { loop_generate_data(); }); 38 | } 39 | void stop() { 40 | m_terminate = true; 41 | if (m_producer_thread) { 42 | m_producer_thread->join(); 43 | m_producer_thread = nullptr; 44 | } 45 | } 46 | void loop_generate_data() { 47 | SchedulingHelper::set_thread_params_max_realtime("DummyStreamGenerator"); 48 | std::chrono::steady_clock::time_point last_packet = 49 | std::chrono::steady_clock::now(); 50 | const uint64_t delay_between_packets_ns = 1000 * 1000 * 1000 / m_target_pps; 51 | const auto delay_between_packets = 52 | std::chrono::nanoseconds(delay_between_packets_ns); 53 | wifibroadcast::log::get_default()->debug( 54 | "Target pps:{} delta between packets:{}", m_target_pps, 55 | MyTimeHelper::R(delay_between_packets)); 56 | while (!m_terminate) { 57 | last_packet = std::chrono::steady_clock::now(); 58 | // wifibroadcast::log::get_default()->debug("Delay between packets: 59 | // {}",std::chrono::duration_cast(delay_between_packets).count()); 60 | auto buff = m_random_buffer_pot->get_next_buffer(); 61 | m_cb(buff->data(), buff->size()); 62 | const auto next_packet_tp = 63 | last_packet + delay_between_packets - 64 | std::chrono::nanoseconds(200); // minus Xns to better hit the target 65 | if (std::chrono::steady_clock::now() >= next_packet_tp) { 66 | // wifibroadcast::log::get_default()->warn("Cannot keep up with the 67 | // wanted tx pps"); 68 | n_times_cannot_keep_up_wanted_pps++; 69 | } 70 | while (std::chrono::steady_clock::now() < next_packet_tp) { 71 | // busy wait 72 | } 73 | } 74 | } 75 | int n_times_cannot_keep_up_wanted_pps = 0; 76 | 77 | private: 78 | const int m_packet_size = 1400; 79 | const OUTPUT_DATA_CALLBACK m_cb; 80 | int m_target_pps = 100; 81 | std::unique_ptr m_producer_thread; 82 | std::unique_ptr m_random_buffer_pot; 83 | bool m_terminate = false; 84 | }; 85 | 86 | #endif // WIFIBROADCAST_DUMMYSTREAMGENERATOR_HPP 87 | -------------------------------------------------------------------------------- /wifibroadcast/src/HelperSources/EmulatedPacketDrop.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EMULATEDPACKETDROP_H 2 | #define EMULATEDPACKETDROP_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | // emulating packet drop "as" when using wifibroadcast (no matter weather it is 10 | // with or without FEC) is not that easy. 11 | 12 | // Drops a specific percentage of packets, this doesn't eumlate the "big gaps" 13 | // behaviour 14 | class PacketDropEmulator { 15 | public: 16 | PacketDropEmulator(int percentage_dropped_packets) 17 | : m_percentage_dropped_packets(percentage_dropped_packets) {} 18 | // Returns true if you should drop this packet, false otherwise 19 | bool drop_packet() { 20 | std::lock_guard lock(m_mutex); 21 | const auto number = next_random_number_0_100(); 22 | // qDebug()<<"Number is:"< number) { 25 | // drop packet 26 | n_dropped_packets++; 27 | log(); 28 | return true; 29 | } 30 | n_forwarded_packets++; 31 | log(); 32 | return false; 33 | } 34 | void log() { 35 | const double perc_dropped = 36 | (double)n_dropped_packets / (n_totoal_packets)*100.0; 37 | // std::cout<<"N 38 | // dropped:"< lock(m_mutex); 42 | m_percentage_dropped_packets = new_perc; 43 | } 44 | 45 | private: 46 | std::mutex m_mutex; 47 | int m_percentage_dropped_packets; 48 | int n_dropped_packets = 0; 49 | int n_forwarded_packets = 0; 50 | int n_totoal_packets = 0; 51 | int next_random_number_0_100() { return m_dist100(m_mt); } 52 | std::mt19937 m_mt; 53 | std::uniform_int_distribution<> m_dist100{0, 100}; 54 | }; 55 | 56 | #endif // EMULATEDPACKETDROP_H 57 | -------------------------------------------------------------------------------- /wifibroadcast/src/HelperSources/README.md: -------------------------------------------------------------------------------- 1 | Here are some generic .hpp files that don't depend on anything than standard library -------------------------------------------------------------------------------- /wifibroadcast/src/HelperSources/RandomBufferPot.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by consti10 on 31.12.21. 3 | // 4 | 5 | #ifndef WIFIBROADCAST_RANDOMBUFFERPOT_H 6 | #define WIFIBROADCAST_RANDOMBUFFERPOT_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "Helper.hpp" 15 | 16 | namespace SemiRandomBuffers { 17 | /** 18 | * Create @param nBuffers buffers of size @param bufferSize filled with 19 | * semi-random data. Each of the generated buffers contains different data than 20 | * the previous generated one (by a almost 100% chance) 21 | */ 22 | static std::vector>> 23 | createSemiRandomBuffers(const std::size_t nBuffers, 24 | const std::size_t bufferSize) { 25 | std::vector>> ret(nBuffers); 26 | for (int i = 0; i < ret.size(); i++) { 27 | ret[i] = std::make_shared>(bufferSize); 28 | } 29 | assert(ret.size() == nBuffers); 30 | // fill all buffers with semi random data 31 | std::mt19937 random_engine(0); 32 | std::uniform_int_distribution<> distrib(0, 256); 33 | 34 | for (auto &buffer : ret) { 35 | assert(buffer->size() == bufferSize); 36 | // NOTE: I think this one doesn't work since the mt19937 is copied with each 37 | // invocation ?! 38 | // std::generate(buffer->data(),buffer->data()+buffer->size(),random_engine); 39 | for (auto &value : *buffer.get()) { 40 | value = distrib(random_engine); 41 | } 42 | } 43 | return ret; 44 | } 45 | // same as above, but different return type 46 | template 47 | static std::vector> createSemiRandomBuffers2( 48 | const std::size_t nBuffers) { 49 | std::vector> ret(nBuffers); 50 | std::mt19937 random_engine(0); 51 | std::uniform_int_distribution<> distrib(0, 256); 52 | for (auto &buffer : ret) { 53 | for (auto &value : buffer) { 54 | value = distrib(random_engine); 55 | } 56 | } 57 | return ret; 58 | } 59 | } // namespace SemiRandomBuffers 60 | 61 | // holds x buffers with (semi-random) data. 62 | class RandomBufferPot { 63 | public: 64 | /** 65 | * Holds @param nBuffers random data buffers of size @param bufferSize 66 | */ 67 | RandomBufferPot(const std::size_t nBuffers, const std::size_t bufferSize) { 68 | m_buffers = 69 | SemiRandomBuffers::createSemiRandomBuffers(nBuffers, bufferSize); 70 | } 71 | // get a semi-random data buffer for this sequence number. If the sequence 72 | // number is higher than the n of allocated buffers, it loops around. As long 73 | // as this pot is big enough, it should be sufficient to emulate a random data 74 | // stream 75 | std::shared_ptr> getBuffer(uint64_t sequenceNumber) { 76 | auto index = sequenceNumber % m_buffers.size(); 77 | return m_buffers.at(index); 78 | } 79 | std::shared_ptr> get_next_buffer() { 80 | return getBuffer(++m_seq_nr); 81 | } 82 | 83 | private: 84 | std::vector>> m_buffers; 85 | // static constexpr const uint32_t SEED=12345; 86 | int m_seq_nr = 0; 87 | }; 88 | 89 | #endif // WIFIBROADCAST_RANDOMBUFFERPOT_H 90 | -------------------------------------------------------------------------------- /wifibroadcast/src/HelperSources/Rates.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by consti10 on 25.07.23. 3 | // 4 | 5 | #ifndef WIFIBROADCAST_RATES_H 6 | #define WIFIBROADCAST_RATES_H 7 | 8 | #include 9 | 10 | namespace wifibroadcast { 11 | 12 | // Theoretical rate(s) 13 | struct Rate { 14 | int rate_20mhz_kbits; 15 | int rate_40mhz_kbits; 16 | }; 17 | 18 | // From https://mcsindex.com/ 19 | static std::vector theoretical_rates_5G() { 20 | return { 21 | Rate{6500, 13500}, // mcs0 (VHT0) 22 | Rate{13000, 27000}, // mcs1 (VHT1) 23 | Rate{19500, 40500}, // mcs2 24 | Rate{26000, 54000}, // mcs3 25 | Rate{39000, 81000}, // mcs4 26 | Rate{52000, 108000}, // mcs5 27 | Rate{58500, 121500}, // mcs6 28 | Rate{65000, 135000}, // mcs7 29 | Rate{13000, 27000}, // mcs8 (VHT0 + SS2) 30 | Rate{26000, 54000}, // mcs9 (VHT1 + SS2) 31 | Rate{39000, 81000}, // mcs10 (VHT2 + SS2) 32 | Rate{52000, 108000}, // mcs11 (VHT3 + SS2) 33 | }; 34 | } 35 | static Rate get_theoretical_rate_5G(int mcs) { 36 | const auto rates = theoretical_rates_5G(); 37 | if (mcs < 0) return {-1, 1}; 38 | if (mcs < rates.size()) { 39 | return rates[mcs]; 40 | } 41 | return rates[rates.size() - 1]; 42 | } 43 | 44 | // Those values come from running the "increase bitrate until tx errors" test 3 45 | // times and taking the lowest (sensible) value of those runs STBC on, LDPC on, 46 | // GUARD LONG ASUS STICK & x86 i7 laptop 47 | static std::vector openhd_rtl8812au_5G_practical_rates() { 48 | return { 49 | Rate{6100, 11700}, // MCS 0 50 | Rate{11000, 20600}, // MCS 1 51 | Rate{16000, 28400}, // MCS 2 52 | Rate{20000, 33400}, // MCS 3 53 | Rate{26000, 36900}, // MCS 4 54 | Rate{28000, 43500}, // MCS 5 55 | Rate{33000, 48000}, // MCS 6 56 | Rate{38000, 53000}, // MCS 7 57 | Rate{11700, 16700}, // MCS 8 (VHT0 + SS2) 58 | Rate{20000, 30000}, // MCS 9 (VHT1 + SS2) 59 | Rate{25000, 37000}, // MCS 10 (VHT2 + SS2) 60 | Rate{30000, 51000}, // MCS 11 (VHT3 + SS2) 61 | }; 62 | } 63 | 64 | static Rate get_practical_rate_5G(int mcs) { 65 | const auto rates = openhd_rtl8812au_5G_practical_rates(); 66 | if (mcs < 0) return {-1, 1}; 67 | if (mcs < rates.size()) { 68 | return rates[mcs]; 69 | } 70 | return rates[rates.size() - 1]; 71 | } 72 | 73 | } // namespace wifibroadcast 74 | 75 | #endif // WIFIBROADCAST_RATES_H 76 | -------------------------------------------------------------------------------- /wifibroadcast/src/HelperSources/SchedulingHelper.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by consti10 on 20.12.20. 3 | // 4 | 5 | #ifndef WIFIBROADCAST_SCHEDULINGHELPER_H 6 | #define WIFIBROADCAST_SCHEDULINGHELPER_H 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | 18 | namespace SchedulingHelper { 19 | 20 | // Only 'low' in comparison to other realtime tasks 21 | static constexpr int PRIORITY_REALTIME_LOW = 30; 22 | static constexpr int PRIORITY_REALTIME_MID = 40; 23 | static constexpr int PRIORITY_REALTIME_HIGH = 50; 24 | 25 | // this thread should run as close to realtime as possible 26 | // https://youtu.be/NrjXEaTSyrw?t=647 27 | // COMMENT: Please don't ever use 99 for your application, there are some kernel 28 | // threads that run at 99 that are really important So ... lets use 90 for now 29 | static void set_thread_params_max_realtime(const std::string& tag, 30 | const int priority = 90) { 31 | pthread_t target = pthread_self(); 32 | int policy = SCHED_FIFO; 33 | sched_param param{}; 34 | // param.sched_priority = sched_get_priority_max(policy); 35 | param.sched_priority = priority; 36 | auto result = pthread_setschedparam(target, policy, ¶m); 37 | 38 | 39 | std::ifstream file("/usr/share/openhd/debug.txt"); 40 | if (file.good()) { 41 | 42 | 43 | 44 | if (result != 0) { 45 | std::stringstream ss; 46 | ss << "Cannot setThreadParamsMaxRealtime " << result; 47 | std::cerr << ss.str() << std::endl; 48 | } else { 49 | std::stringstream ss; 50 | ss << "Changed prio "; 51 | if (!tag.empty()) { 52 | ss << "for " << tag << " "; 53 | } 54 | ss << "to SCHED_FIFO:" << param.sched_priority; 55 | std::cout << ss.str() << std::endl; 56 | } 57 | } 58 | } 59 | 60 | static bool check_root() { 61 | const auto uid = getuid(); 62 | const bool root = uid ? false : true; 63 | return root; 64 | } 65 | 66 | } // namespace SchedulingHelper 67 | #endif // WIFIBROADCAST_SCHEDULINGHELPER_H 68 | -------------------------------------------------------------------------------- /wifibroadcast/src/HelperSources/SequenceNumberDebugger.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by consti10 on 18.05.22. 3 | // 4 | 5 | #ifndef WIFIBROADCAST_SRC_HELPERSOURCES_SEQUENCENUMBERDEBUGGER_H_ 6 | #define WIFIBROADCAST_SRC_HELPERSOURCES_SEQUENCENUMBERDEBUGGER_H_ 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "../wifibroadcast_spdlog.h" 16 | #include "StringHelper.hpp" 17 | 18 | /** 19 | * Debug the n lost packets and the n of packet gaps by for a continuous stream 20 | * of packets with increasing sequence number. 21 | */ 22 | class SequenceNumberDebugger { 23 | public: 24 | SequenceNumberDebugger() { gapsBetweenLostPackets.reserve(1000); } 25 | /** 26 | * Call when a new squence number is received 27 | * @param seqNr the received sequence number. 28 | */ 29 | void sequenceNumber(const int64_t seqNr) { 30 | nReceivedPackets++; 31 | auto delta = seqNr - lastReceivedSequenceNr; 32 | if (delta <= 0) { 33 | wifibroadcast::log::get_default()->debug( 34 | "got packet nr: {} after packet nr: {}", seqNr, 35 | lastReceivedSequenceNr); 36 | return; 37 | } 38 | if (delta > 1) { 39 | nLostPackets += delta - 1; 40 | gapsBetweenLostPackets.push_back(delta); 41 | } 42 | if (gapsBetweenLostPackets.size() >= 1000) { 43 | gapsBetweenLostPackets.resize(0); 44 | } 45 | lastReceivedSequenceNr = seqNr; 46 | } 47 | /** 48 | * Log information about the lost packets and gaps between them. 49 | * @param clear clear the already accumulated data. 50 | */ 51 | void debug(bool clear) { 52 | std::stringstream ss; 53 | ss << "N packets received:" << nReceivedPackets << "\tlost:" << nLostPackets 54 | << "\n"; 55 | ss << "Packet gaps:" 56 | << StringHelper::vectorAsString(gapsBetweenLostPackets); 57 | wifibroadcast::log::get_default()->debug(ss.str()); 58 | if (clear) { 59 | gapsBetweenLostPackets.resize(0); 60 | } 61 | } 62 | void debug_in_intervals() { 63 | const auto elapsed = std::chrono::steady_clock::now() - m_last_log; 64 | if (elapsed < std::chrono::seconds(1)) { 65 | return; 66 | } 67 | debug(true); 68 | m_last_log = std::chrono::steady_clock::now(); 69 | } 70 | 71 | private: 72 | std::int64_t lastReceivedSequenceNr = -1; 73 | std::int64_t nReceivedPackets = 0; 74 | std::int64_t nLostPackets = 0; 75 | std::vector gapsBetweenLostPackets; 76 | std::chrono::steady_clock::time_point m_last_log = 77 | std::chrono::steady_clock::now(); 78 | }; 79 | 80 | #endif // WIFIBROADCAST_SRC_HELPERSOURCES_SEQUENCENUMBERDEBUGGER_H_ 81 | -------------------------------------------------------------------------------- /wifibroadcast/src/HelperSources/StringHelper.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Constantin on 09.10.2017. 3 | // 4 | 5 | #ifndef OSDTESTER_STRINGHELPER_H 6 | #define OSDTESTER_STRINGHELPER_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | class StringHelper { 14 | public: 15 | template 16 | static std::string vectorAsString(const std::vector& v) { 17 | std::stringstream ss; 18 | ss << "["; 19 | for (int i = 0; i < v.size(); i++) { 20 | ss << std::to_string(v[i]); 21 | if (i != v.size() - 1) { 22 | ss << ","; 23 | } 24 | } 25 | ss << "]"; 26 | return ss.str(); 27 | } 28 | 29 | static std::string string_vec_as_string(const std::vector& v) { 30 | std::stringstream ss; 31 | ss << "["; 32 | for (int i = 0; i < v.size(); i++) { 33 | ss << v[i]; 34 | if (i != v.size() - 1) { 35 | ss << ","; 36 | } 37 | } 38 | ss << "]"; 39 | return ss.str(); 40 | } 41 | static std::string bytes_as_string_decimal(const uint8_t* data, 42 | int data_len) { 43 | std::stringstream ss; 44 | ss << "["; 45 | for (int i = 0; i < data_len; i++) { 46 | ss << (int)data[i]; 47 | if (i != data_len - 1) { 48 | ss << ","; 49 | } 50 | } 51 | ss << "]"; 52 | return ss.str(); 53 | } 54 | static std::string byte_as_hex(uint8_t byte) { 55 | char str[100] = {}; 56 | sprintf(str, "%x", byte); 57 | return "0x" + std::string(str); 58 | } 59 | static std::string bytes_as_string_hex(const uint8_t* data, int data_len) { 60 | std::stringstream ss; 61 | ss << "["; 62 | for (int i = 0; i < data_len; i++) { 63 | ss << byte_as_hex(data[i]); 64 | if (i != data_len - 1) { 65 | ss << ","; 66 | } 67 | } 68 | ss << "]"; 69 | return ss.str(); 70 | } 71 | 72 | template 73 | static std::string arrayAsString(const std::array& a) { 74 | std::stringstream ss; 75 | ss << "["; 76 | for (int i = 0; i < a.size(); i++) { 77 | ss << std::to_string(a[i]); 78 | if (i != a.size() - 1) { 79 | ss << ","; 80 | } 81 | } 82 | ss << "]"; 83 | return ss.str(); 84 | } 85 | 86 | static std::string memorySizeReadable(const size_t sizeBytes) { 87 | // more than one MB 88 | if (sizeBytes > 1024 * 1024) { 89 | float sizeMB = (float)sizeBytes / 1024.0 / 1024.0; 90 | return std::to_string(sizeMB) + "mB"; 91 | } 92 | // more than one KB 93 | if (sizeBytes > 1024) { 94 | float sizeKB = (float)sizeBytes / 1024.0; 95 | return std::to_string(sizeKB) + "kB"; 96 | } 97 | return std::to_string(sizeBytes) + "B"; 98 | } 99 | 100 | static std::string float_to_string_with_precision(float value, 101 | int precision = -1) { 102 | if (precision == -1) { 103 | return std::to_string(value); 104 | } 105 | std::stringstream ss; 106 | ss.precision(precision); 107 | ss << std::fixed << value; 108 | return ss.str(); 109 | } 110 | 111 | static std::string bitrate_readable(int64_t bits_per_second) { 112 | if (bits_per_second <= 0) { 113 | return std::to_string(bits_per_second) + " Bit/s"; 114 | } 115 | if (bits_per_second > 1024 * 1024) { 116 | float mBitsPerSecond = (float)bits_per_second / 1000.0 / 1000.0; 117 | return float_to_string_with_precision(mBitsPerSecond, 2) + "mBit/s"; 118 | } 119 | if (bits_per_second > 1024) { 120 | float kBitsPerSecond = (float)bits_per_second / 1000.0; 121 | return float_to_string_with_precision(kBitsPerSecond, 2) + "kBit/s"; 122 | } 123 | return float_to_string_with_precision(bits_per_second, 2) + "Bit/s"; 124 | } 125 | }; 126 | 127 | #endif // OSDTESTER_STRINGHELPER_H 128 | -------------------------------------------------------------------------------- /wifibroadcast/src/WBVideoStreamTx.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by consti10 on 06.01.24. 3 | // 4 | 5 | #include "WBVideoStreamTx.h" 6 | 7 | #include "BlockSizeHelper.hpp" 8 | #include "SchedulingHelper.hpp" 9 | 10 | struct CodecConfigPacket { 11 | uint8_t codec_type; 12 | uint16_t config_data_len; 13 | // config_data_len bytes follow 14 | }; 15 | 16 | WBVideoStreamTx::WBVideoStreamTx( 17 | std::shared_ptr txrx, WBVideoStreamTx::Options options1, 18 | std::shared_ptr radiotap_header_holder) 19 | : options(options1), 20 | m_txrx(txrx), 21 | m_radiotap_header_holder(std::move(radiotap_header_holder)) { 22 | assert(m_txrx); 23 | if (options.opt_console) { 24 | m_console = options.opt_console; 25 | } else { 26 | m_console = wifibroadcast::log::create_or_get( 27 | "wb_tx" + std::to_string(options.radio_port)); 28 | } 29 | assert(m_console); 30 | m_block_queue = std::make_unique(options.frame_queue_size); 31 | m_fec_encoder = std::make_unique(); 32 | auto cb = [this](const uint8_t* packet, int packet_len) { 33 | send_packet(packet, packet_len); 34 | }; 35 | m_fec_encoder->m_out_cb = cb; 36 | m_process_data_thread_run = true; 37 | m_process_data_thread = 38 | std::make_unique(&WBVideoStreamTx::loop_process_data, this); 39 | } 40 | 41 | WBVideoStreamTx::~WBVideoStreamTx() { 42 | m_process_data_thread_run = false; 43 | if (m_process_data_thread && m_process_data_thread->joinable()) { 44 | m_process_data_thread->join(); 45 | } 46 | } 47 | 48 | void WBVideoStreamTx::set_config_data( 49 | uint8_t codec_type, std::shared_ptr> config_buff) { 50 | auto config = std::make_shared(); 51 | config->codec_type = codec_type; 52 | config->config_buff = config_buff; 53 | std::lock_guard guard(m_codec_config_mutex); 54 | m_codec_config = config; 55 | } 56 | 57 | bool WBVideoStreamTx::enqueue_frame( 58 | std::shared_ptr> frame, int max_block_size, 59 | int fec_overhead_perc, 60 | std::chrono::steady_clock::time_point creation_time) { 61 | auto item = std::make_shared(); 62 | item->frame = frame; 63 | item->max_block_size = max_block_size; 64 | item->fec_overhead_perc = fec_overhead_perc; 65 | item->creation_time = creation_time; 66 | const bool res = m_block_queue->try_enqueue(item); 67 | return res; 68 | } 69 | 70 | void WBVideoStreamTx::loop_process_data() { 71 | if (options.dequeue_thread_max_realtime) { 72 | SchedulingHelper::set_thread_params_max_realtime("WBVideoStreamTx::loop"); 73 | } 74 | std::chrono::steady_clock::time_point last_config = 75 | std::chrono::steady_clock::now(); 76 | while (m_process_data_thread_run) { 77 | auto opt_frame = 78 | m_block_queue->wait_dequeue_timed(std::chrono::milliseconds(100)); 79 | if (opt_frame.has_value()) { 80 | auto frame = opt_frame.value(); 81 | process_enqueued_frame(*frame); 82 | } 83 | const auto now = std::chrono::steady_clock::now(); 84 | if (now - last_config >= options.codec_config_interval) { 85 | if (send_video_config()) { 86 | last_config = now; 87 | } 88 | } 89 | } 90 | } 91 | 92 | void WBVideoStreamTx::process_enqueued_frame(const EnqueuedFrame& enq_frame) { 93 | // TODO: Figure out the ideal fragment size for this frame 94 | const int n_primary_fragments = 95 | blocksize::div_ceil(enq_frame.frame->size(), FEC_PACKET_MAX_PAYLOAD_SIZE); 96 | const int n_secondary_fragments = calculate_n_secondary_fragments( 97 | n_primary_fragments, enq_frame.fec_overhead_perc); 98 | m_fec_encoder->fragment_and_encode( 99 | enq_frame.frame->data(), enq_frame.frame->size(), n_primary_fragments, 100 | n_secondary_fragments); 101 | } 102 | 103 | void WBVideoStreamTx::send_packet(const uint8_t* packet, int packet_len) { 104 | const auto radiotap_header = m_radiotap_header_holder->thread_safe_get(); 105 | const bool encrypt = m_enable_encryption.load(); 106 | m_txrx->tx_inject_packet(options.radio_port, packet, packet_len, 107 | radiotap_header, encrypt); 108 | } 109 | 110 | bool WBVideoStreamTx::send_video_config() { 111 | std::lock_guard guard(m_codec_config_mutex); 112 | if (m_codec_config == nullptr) return false; 113 | auto& config_buff = *m_codec_config->config_buff; 114 | assert(!config_buff.empty() && config_buff.size() < MAX_PAYLOAD_BEFORE_FEC); 115 | m_fec_encoder->fragment_and_encode(config_buff.data(), config_buff.size(), 1, 116 | 0); 117 | return true; 118 | } 119 | -------------------------------------------------------------------------------- /wifibroadcast/src/WBVideoStreamTx.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by consti10 on 06.01.24. 3 | // 4 | 5 | #ifndef WIFIBROADCAST_WBVIDEOSTREAMTX_H 6 | #define WIFIBROADCAST_WBVIDEOSTREAMTX_H 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include "../fec/FEC.h" 13 | #include "../fec/FECEncoder.h" 14 | #include "FunkyQueue.h" 15 | #include "SimpleStream.hpp" 16 | #include "TimeHelper.hpp" 17 | #include "WBTxRx.h" 18 | 19 | class WBVideoStreamTx { 20 | public: 21 | struct Options { 22 | // needs to match the radio port of the corresponding rx 23 | uint8_t radio_port = 0; 24 | int frame_queue_size = 2; 25 | // overwrite the console used for logging 26 | std::shared_ptr opt_console = nullptr; 27 | // set sched_param = max realtime on the thread that dequeues and injects 28 | // the packets 29 | bool dequeue_thread_max_realtime = true; 30 | std::chrono::milliseconds codec_config_interval = std::chrono::seconds(1); 31 | }; 32 | WBVideoStreamTx( 33 | std::shared_ptr txrx, Options options, 34 | std::shared_ptr radiotap_header_holder); 35 | WBVideoStreamTx(const WBVideoStreamTx&) = delete; 36 | WBVideoStreamTx& operator=(const WBVideoStreamTx&) = delete; 37 | ~WBVideoStreamTx(); 38 | void set_config_data(uint8_t codec_type, 39 | std::shared_ptr> config_buff); 40 | bool enqueue_frame(std::shared_ptr> frame, 41 | int max_block_size, int fec_overhead_perc, 42 | std::chrono::steady_clock::time_point creation_time = 43 | std::chrono::steady_clock::now()); 44 | std::atomic_int32_t in_fps = 0; 45 | std::atomic_int32_t in_bps = 0; 46 | std::atomic_int32_t out_pps = 0; 47 | 48 | private: 49 | struct EnqueuedFrame { 50 | std::chrono::steady_clock::time_point enqueue_time_point = 51 | std::chrono::steady_clock::now(); 52 | std::chrono::steady_clock::time_point creation_time = 53 | std::chrono::steady_clock::now(); 54 | int max_block_size; 55 | int fec_overhead_perc; 56 | std::shared_ptr> frame = nullptr; 57 | }; 58 | struct CodecConfigData { 59 | uint8_t codec_type; 60 | std::shared_ptr> config_buff = nullptr; 61 | }; 62 | 63 | private: 64 | const Options options; 65 | std::shared_ptr m_txrx; 66 | std::shared_ptr m_radiotap_header_holder; 67 | std::shared_ptr m_console; 68 | // On the tx, either one of those two is active at the same time 69 | std::unique_ptr m_fec_encoder = nullptr; 70 | std::unique_ptr m_process_data_thread; 71 | using FrameQueueType = FunkyQueue>; 72 | std::unique_ptr m_block_queue; 73 | bool m_process_data_thread_run = true; 74 | void loop_process_data(); 75 | void process_enqueued_frame(const EnqueuedFrame& enq_frame); 76 | void send_packet(const uint8_t* packet, int packet_len); 77 | bool send_video_config(); 78 | std::shared_ptr m_codec_config = nullptr; 79 | std::mutex m_codec_config_mutex; 80 | std::atomic m_enable_encryption = true; 81 | }; 82 | 83 | #endif // WIFIBROADCAST_WBVIDEOSTREAMTX_H 84 | -------------------------------------------------------------------------------- /wifibroadcast/src/WiFiCard.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by consti10 on 13.09.23. 3 | // 4 | 5 | #ifndef WIFIBROADCAST_WIFICARD_H 6 | #define WIFIBROADCAST_WIFICARD_H 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | namespace wifibroadcast { 13 | // In OpenHD, we have a quite extensive WiFiCard abstraction - 14 | // in wifibroadcast, we are a bit simpler 15 | // (But we require info for quirks) 16 | // RTL8812AU driver requires a quirk regarding rssi 17 | static constexpr auto WIFI_CARD_TYPE_UNKNOWN = 0; 18 | static constexpr auto WIFI_CARD_TYPE_RTL8812AU = 1; 19 | static constexpr auto WIFI_CARD_TYPE_EMULATE_AIR = 2; 20 | static constexpr auto WIFI_CARD_TYPE_EMULATE_GND = 3; 21 | struct WifiCard { 22 | std::string name; 23 | int type; 24 | }; 25 | static std::vector get_wifi_card_names( 26 | const std::vector& cards) { 27 | std::vector ret; 28 | for (const auto& card : cards) { 29 | ret.push_back(card.name); 30 | } 31 | return ret; 32 | } 33 | 34 | static WifiCard create_card_emulate(bool is_air_card) { 35 | return WifiCard{ 36 | is_air_card ? "emu_air" : "emu_gnd", 37 | is_air_card ? WIFI_CARD_TYPE_EMULATE_AIR : WIFI_CARD_TYPE_EMULATE_GND}; 38 | } 39 | } // namespace wifibroadcast 40 | 41 | #endif // WIFIBROADCAST_WIFICARD_H 42 | -------------------------------------------------------------------------------- /wifibroadcast/src/dummy_link/DummyLink.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by consti10 on 07.01.24. 3 | // 4 | 5 | #ifndef OPENHD_DUMMYLINK_H 6 | #define OPENHD_DUMMYLINK_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "../FunkyQueue.h" 19 | 20 | // TODO: Write something that emulates a wb link (tx, rx) 21 | // using linux shm or similar 22 | class DummyLink { 23 | public: 24 | explicit DummyLink(bool is_air); 25 | ~DummyLink(); 26 | void tx_radiotap(const uint8_t* packet_buff, int packet_size); 27 | std::shared_ptr> rx_radiotap(); 28 | void set_drop_mode(int drop_mode); 29 | 30 | private: 31 | const bool m_is_air; 32 | int m_fd_tx; 33 | int m_fd_rx; 34 | std::string m_fn_tx; 35 | std::string m_fn_rx; 36 | std::unique_ptr m_receive_thread; 37 | void loop_rx(); 38 | bool m_keep_receiving = true; 39 | // Drop packets with a probability of 5% 40 | bool should_drop_packet(); 41 | int next_random_number_0_100() { return m_dist100(m_mt); } 42 | std::mt19937 m_mt; 43 | std::uniform_int_distribution<> m_dist100{0, 100}; 44 | struct RxPacket { 45 | std::shared_ptr> buff; 46 | }; 47 | using RxPacketQueueType = FunkyQueue>; 48 | std::unique_ptr m_rx_queue; 49 | std::atomic_int m_drop_mode = 0; 50 | }; 51 | 52 | #endif // OPENHD_DUMMYLINK_H 53 | -------------------------------------------------------------------------------- /wifibroadcast/src/encryption/Decryptor.cpp: -------------------------------------------------------------------------------- 1 | #include "Decryptor.h" 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include "../wifibroadcast_spdlog.h" 9 | #include "Encryption.h" 10 | 11 | wb::Decryptor::Decryptor(wb::Key key1) 12 | : rx_secretkey(key1.secret_key), tx_publickey(key1.public_key) { 13 | memset(session_key.data(), 0, sizeof(session_key)); 14 | } 15 | 16 | int wb::Decryptor::onNewPacketSessionKeyData( 17 | const std::array& sessionKeyNonce, 18 | const std::array& sessionKeyData) { 19 | std::array new_session_key{}; 20 | if (crypto_box_open_easy(new_session_key.data(), sessionKeyData.data(), 21 | sessionKeyData.size(), sessionKeyNonce.data(), 22 | tx_publickey.data(), rx_secretkey.data()) != 0) { 23 | // this basically should just never happen, and is an error 24 | wifibroadcast::log::get_default()->warn("unable to decrypt session key"); 25 | return SESSION_NOT_VALID; 26 | } 27 | if (memcmp(session_key.data(), new_session_key.data(), sizeof(session_key)) != 28 | 0) { 29 | wifibroadcast::log::get_default()->info("Decryptor-New session detected"); 30 | session_key = new_session_key; 31 | m_has_valid_session = true; 32 | return SESSION_VALID_NEW; 33 | } 34 | // this is NOT an error, the same session key is sent multiple times ! 35 | return SESSION_VALID_NOT_NEW; 36 | } 37 | 38 | bool wb::Decryptor::authenticate(const uint64_t& nonce, 39 | const uint8_t* encrypted, int encrypted_size, 40 | uint8_t* dest) { 41 | const auto payload_size = encrypted_size - crypto_onetimeauth_BYTES; 42 | assert(payload_size > 0); 43 | const uint8_t* sign = encrypted + payload_size; 44 | // const int 45 | // res=crypto_auth_hmacsha256_verify(sign,msg,payload_size,session_key.data()); 46 | const auto sub_key = wb::create_onetimeauth_subkey(nonce, session_key); 47 | const int res = 48 | crypto_onetimeauth_verify(sign, encrypted, payload_size, sub_key.data()); 49 | if (res != -1) { 50 | memcpy(dest, encrypted, payload_size); 51 | return true; 52 | } 53 | return false; 54 | } 55 | 56 | bool wb::Decryptor::decrypt(const uint64_t& nonce, const uint8_t* encrypted, 57 | int encrypted_size, uint8_t* dest) { 58 | unsigned long long mlen; 59 | int res = crypto_aead_chacha20poly1305_decrypt( 60 | dest, &mlen, nullptr, encrypted, encrypted_size, nullptr, 0, 61 | (uint8_t*)(&nonce), session_key.data()); 62 | return res != -1; 63 | } 64 | 65 | std::shared_ptr> 66 | wb::Decryptor::authenticate_and_decrypt_buff(const uint64_t& nonce, 67 | const uint8_t* encrypted, 68 | int encrypted_size, 69 | bool isEncrypt) { 70 | auto ret = std::make_shared>( 71 | encrypted_size - crypto_aead_chacha20poly1305_ABYTES); 72 | 73 | bool res; 74 | if (isEncrypt) { 75 | res = decrypt(nonce, encrypted, encrypted_size, ret->data()); 76 | } else { 77 | res = authenticate(nonce, encrypted, encrypted_size, ret->data()); 78 | } 79 | 80 | if (res) { 81 | return ret; 82 | } 83 | return nullptr; 84 | } -------------------------------------------------------------------------------- /wifibroadcast/src/encryption/Decryptor.h: -------------------------------------------------------------------------------- 1 | #ifndef DECRIPTOR_HPP 2 | #define DECRIPTOR_HPP 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "KeyPair.h" 12 | 13 | namespace wb { 14 | class Decryptor { 15 | public: 16 | // enable a default deterministic encryption key by using std::nullopt 17 | // else, pass path to file with encryption keys 18 | explicit Decryptor(wb::Key key1); 19 | static constexpr auto SESSION_VALID_NEW = 0; 20 | static constexpr auto SESSION_VALID_NOT_NEW = 1; 21 | static constexpr auto SESSION_NOT_VALID = -1; 22 | /** 23 | * Returns 0 if the session is a valid session in regards to the key-pairs AND 24 | * the session is a new session Returns 1 if the session is a valid session in 25 | * regards to the key-pairs but it is not a new session Returns -1 if the 26 | * session is not a valid session in regards to the key-pairs 27 | * 28 | */ 29 | int onNewPacketSessionKeyData( 30 | const std::array& sessionKeyNonce, 31 | const std::array& sessionKeyData); 33 | 34 | /** 35 | * Decrypt the given message 36 | * and writes the original message content into dest. 37 | * Returns true on success, false otherwise (false== the message is not a 38 | * valid message) 39 | * @param dest needs to be at least @param encrypted - 16 bytes big. 40 | */ 41 | bool decrypt(const uint64_t& nonce, const uint8_t* encrypted, 42 | int encrypted_size, uint8_t* dest); 43 | 44 | /** 45 | * Validate only the given message 46 | * and writes the original message content into dest. 47 | * Returns true on success, false otherwise (false== the message is not a 48 | * valid message) 49 | * @param dest needs to be at least @param encrypted - 16 bytes big. 50 | */ 51 | bool authenticate(const uint64_t& nonce, const uint8_t* encrypted, 52 | int encrypted_size, uint8_t* dest); 53 | 54 | /** 55 | * Easier to use, but usage might require memcpy 56 | * For test use 57 | */ 58 | std::shared_ptr> authenticate_and_decrypt_buff( 59 | const uint64_t& nonce, const uint8_t* encrypted, int encrypted_size, 60 | bool isEncrypt); 61 | 62 | // Set to true as soon as a valid session has been detected 63 | bool has_valid_session() const { return m_has_valid_session; } 64 | 65 | private: 66 | const std::array rx_secretkey{}; 67 | const std::array tx_publickey{}; 68 | std::array session_key{}; 69 | bool m_has_valid_session = false; 70 | }; 71 | 72 | } // namespace wb 73 | 74 | #endif // DECRIPTOR_HPP -------------------------------------------------------------------------------- /wifibroadcast/src/encryption/Encryption.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by consti10 on 13.08.23. 3 | // 4 | 5 | #include "Encryption.h" 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #include "../wifibroadcast_spdlog.h" 14 | 15 | wb::KeyPairTxRx wb::generate_keypair_random() { 16 | KeyPairTxRx ret{}; 17 | crypto_box_keypair(ret.key_1.public_key.data(), ret.key_1.secret_key.data()); 18 | crypto_box_keypair(ret.key_2.public_key.data(), ret.key_2.secret_key.data()); 19 | return ret; 20 | } 21 | 22 | // Salts generated once using 23 | // https://www.random.org/cgi-bin/randbyte?nbytes=16&format=d We want 24 | // deterministic seed from a pw, and are only interested in making it impossible 25 | // to reverse the process (even though the salt is plain text) 26 | static constexpr std::array OHD_SALT_AIR{ 27 | 192, 189, 216, 102, 56, 153, 154, 92, 228, 26, 49, 209, 157, 7, 128, 207}; 28 | static constexpr std::array OHD_SALT_GND{ 29 | 179, 30, 150, 20, 17, 200, 225, 82, 48, 64, 18, 130, 89, 62, 83, 234}; 30 | 31 | std::array 32 | wb::create_seed_from_password_openhd_salt(const std::string& pw, 33 | bool use_salt_air) { 34 | const auto salt = use_salt_air ? OHD_SALT_AIR : OHD_SALT_GND; 35 | std::array key{}; 36 | if (crypto_pwhash(key.data(), key.size(), pw.c_str(), pw.length(), 37 | salt.data(), crypto_pwhash_OPSLIMIT_INTERACTIVE, 38 | crypto_pwhash_MEMLIMIT_INTERACTIVE, 39 | crypto_pwhash_ALG_DEFAULT) != 0) { 40 | std::cerr << "ERROR: cannot create_seed_from_password_openhd_salt" 41 | << std::endl; 42 | assert(false); 43 | // out of memory 44 | } 45 | return key; 46 | } 47 | 48 | wb::KeyPairTxRx wb::generate_keypair_from_bind_phrase( 49 | const std::string& bind_phrase) { 50 | const auto seed_air = 51 | create_seed_from_password_openhd_salt(bind_phrase, true); 52 | const auto seed_gnd = 53 | create_seed_from_password_openhd_salt(bind_phrase, false); 54 | KeyPairTxRx ret{}; 55 | crypto_box_seed_keypair(ret.key_1.public_key.data(), 56 | ret.key_1.secret_key.data(), seed_air.data()); 57 | crypto_box_seed_keypair(ret.key_2.public_key.data(), 58 | ret.key_2.secret_key.data(), seed_gnd.data()); 59 | return ret; 60 | } 61 | 62 | std::array 63 | wb::create_onetimeauth_subkey(const uint64_t& nonce, 64 | const std::array& session_key) { 65 | // sub-key for this packet 66 | std::array subkey{}; 67 | // We only have an 8 byte nonce, this should be enough entropy 68 | std::array nonce_buf{0}; 69 | memcpy(nonce_buf.data(), (uint8_t*)&nonce, 8); 70 | crypto_core_hchacha20(subkey.data(), nonce_buf.data(), session_key.data(), 71 | nullptr); 72 | return subkey; 73 | } -------------------------------------------------------------------------------- /wifibroadcast/src/encryption/Encryption.h: -------------------------------------------------------------------------------- 1 | #ifndef ENCRYPTION_HPP 2 | #define ENCRYPTION_HPP 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "KeyPair.h" 12 | 13 | // Namespace that can be used to add encryption+packet validation 14 | // (Or packet validation only to save CPU resources) 15 | // to a lossy unidirectional link 16 | // Packet validation is quite important, to make sure only openhd packets (and 17 | // not standard wifi packets) are used in OpenHD The Encryption / Decryption 18 | // name(s) are legacy - The more difficult part is dealing with the session key 19 | // stuff, and this class makes it a bit easier to use 20 | 21 | // one time authentication and encryption nicely are really similar 22 | static_assert(crypto_onetimeauth_BYTES == crypto_aead_chacha20poly1305_ABYTES); 23 | // Encryption (or packet validation) adds this many bytes to the end of the 24 | // message 25 | static constexpr auto ENCRYPTION_ADDITIONAL_VALIDATION_DATA = 26 | crypto_aead_chacha20poly1305_ABYTES; 27 | 28 | namespace wb { 29 | 30 | /** 31 | * Generates a new keypair. Non-deterministic, 100% secure. 32 | */ 33 | KeyPairTxRx generate_keypair_random(); 34 | 35 | /** 36 | * See https://libsodium.gitbook.io/doc/password_hashing 37 | * Deterministic seed from password, but hides password itself (non-reversible) 38 | * Uses a pre-defined salt to be deterministic 39 | */ 40 | std::array create_seed_from_password_openhd_salt( 41 | const std::string& pw, bool use_salt_air); 42 | 43 | // We always use the same bind phrase by default 44 | static constexpr auto DEFAULT_BIND_PHRASE = "openhd"; 45 | /** 46 | * Generates 2 new (deterministic) tx rx keys, using the seed created from the 47 | * pw. 48 | * @param bind_phrase the password / bind phrase 49 | */ 50 | KeyPairTxRx generate_keypair_from_bind_phrase( 51 | const std::string& bind_phrase = DEFAULT_BIND_PHRASE); 52 | 53 | /** 54 | * https://libsodium.gitbook.io/doc/key_derivation 55 | * UINT16SeqNrHelper since we both support encryption and one time validation to 56 | * save cpu performance 57 | */ 58 | std::array create_onetimeauth_subkey( 59 | const uint64_t& nonce, 60 | const std::array& 61 | session_key); 62 | 63 | } // namespace wb 64 | 65 | #endif // ENCRYPTION_HPP -------------------------------------------------------------------------------- /wifibroadcast/src/encryption/EncryptionFsUtils.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by consti10 on 13.08.23. 3 | // 4 | 5 | #include "EncryptionFsUtils.h" 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #include "../wifibroadcast_spdlog.h" 15 | 16 | bool wb::write_keypair_to_file(const wb::KeyPairTxRx& keypair_txrx, 17 | const std::string& filename) { 18 | FILE* fp; 19 | if ((fp = fopen(filename.c_str(), "w")) == nullptr) { 20 | std::cerr << "Unable to save " << filename << std::endl; 21 | return false; 22 | } 23 | const auto raw = KeyPairTxRx::as_raw(keypair_txrx); 24 | auto res = fwrite(raw.data(), raw.size(), 1, fp); 25 | if (res != 1) { 26 | std::cerr << "Cannot write to file" << std::endl; 27 | fclose(fp); 28 | return false; 29 | } 30 | fclose(fp); 31 | return true; 32 | } 33 | 34 | std::optional wb::read_keypair_from_file( 35 | const std::string& filename) { 36 | KeyPairTxRx ret{}; 37 | FILE* fp; 38 | if ((fp = fopen(filename.c_str(), "r")) == nullptr) { 39 | std::cerr << fmt::format("Unable to open {}: {}", filename.c_str(), 40 | strerror(errno)) 41 | << std::endl; 42 | return std::nullopt; 43 | } 44 | std::array raw{}; 45 | auto res = fread(raw.data(), raw.size(), 1, fp); 46 | if (res != 1) { 47 | std::cerr << "Cannot read keypair file" << std::endl; 48 | fclose(fp); 49 | return std::nullopt; 50 | } 51 | fclose(fp); 52 | return KeyPairTxRx::from_raw(raw); 53 | } -------------------------------------------------------------------------------- /wifibroadcast/src/encryption/EncryptionFsUtils.h: -------------------------------------------------------------------------------- 1 | #ifndef ENCRYPTION_FS_UTILS_HPP 2 | #define ENCRYPTION_FS_UTILS_HPP 3 | 4 | #include 5 | #include 6 | 7 | #include "KeyPair.h" 8 | 9 | namespace wb { 10 | 11 | /** 12 | * Saves the KeyPairTxRx as a raw file 13 | */ 14 | bool write_keypair_to_file(const KeyPairTxRx& keypair_txrx, 15 | const std::string& filename); 16 | 17 | /** 18 | * Reads a raw KeyPairTxRx from a raw file previusly generated. 19 | */ 20 | std::optional read_keypair_from_file(const std::string& filename); 21 | 22 | } // namespace wb 23 | 24 | #endif // ENCRYPTION_FS_UTILS_HPP -------------------------------------------------------------------------------- /wifibroadcast/src/encryption/Encryptor.cpp: -------------------------------------------------------------------------------- 1 | #include "Encryptor.h" 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "Encryption.h" 10 | 11 | void wb::Encryptor::makeNewSessionKey( 12 | std::array& sessionKeyNonce, 13 | std::array& sessionKeyData) { 14 | randombytes_buf(session_key.data(), sizeof(session_key)); 15 | randombytes_buf(sessionKeyNonce.data(), sizeof(sessionKeyNonce)); 16 | if (crypto_box_easy(sessionKeyData.data(), session_key.data(), 17 | sizeof(session_key), sessionKeyNonce.data(), 18 | rx_publickey.data(), tx_secretkey.data()) != 0) { 19 | throw std::runtime_error("Unable to make session key!"); 20 | } 21 | } 22 | 23 | int wb::Encryptor::authenticate_and_encrypt(const uint64_t& nonce, 24 | const uint8_t* src, int src_len, 25 | uint8_t* dest) { 26 | if (!m_encrypt_data) { // Only sign message 27 | memcpy(dest, src, src_len); 28 | uint8_t* sign = dest + src_len; 29 | const auto sub_key = wb::create_onetimeauth_subkey(nonce, session_key); 30 | crypto_onetimeauth(sign, src, src_len, sub_key.data()); 31 | return src_len + crypto_onetimeauth_BYTES; 32 | } 33 | // sign and encrypt all together 34 | long long unsigned int ciphertext_len; 35 | crypto_aead_chacha20poly1305_encrypt(dest, &ciphertext_len, src, src_len, 36 | (uint8_t*)nullptr, 0, nullptr, 37 | (uint8_t*)&nonce, session_key.data()); 38 | return (int)ciphertext_len; 39 | } 40 | 41 | std::shared_ptr> 42 | wb::Encryptor::authenticate_and_encrypt_buff(const uint64_t& nonce, 43 | const uint8_t* src, 44 | std::size_t src_len) { 45 | auto ret = std::make_shared>( 46 | src_len + ENCRYPTION_ADDITIONAL_VALIDATION_DATA); 47 | const auto size = authenticate_and_encrypt(nonce, src, src_len, ret->data()); 48 | assert(size == ret->size()); 49 | return ret; 50 | } -------------------------------------------------------------------------------- /wifibroadcast/src/encryption/Encryptor.h: -------------------------------------------------------------------------------- 1 | #ifndef ENCRIPTOR_HPP 2 | #define ENCRIPTOR_HPP 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "KeyPair.h" 13 | 14 | namespace wb { 15 | class Encryptor { 16 | public: 17 | /** 18 | * 19 | * @param key1 encryption key, otherwise enable a default deterministic 20 | * encryption key by using std::nullopt 21 | * @param DISABLE_ENCRYPTION_FOR_PERFORMANCE only validate, do not encrypt 22 | * (less CPU usage) 23 | */ 24 | explicit Encryptor(wb::Key key1) 25 | : tx_secretkey(key1.secret_key), rx_publickey(key1.public_key) {} 26 | /** 27 | * Creates a new session key, simply put, the data we can send publicly 28 | * @param sessionKeyNonce filled with public nonce 29 | * @param sessionKeyData filled with public data 30 | */ 31 | void makeNewSessionKey( 32 | std::array &sessionKeyNonce, 33 | std::array &sessionKeyData); 35 | /** 36 | * Encrypt the given message of size @param src_len 37 | * (Or if encryption is disabled, only calculate the message sign) 38 | * and write the (encrypted) data appended by the validation data into dest 39 | * @param nonce: needs to be different for every packet 40 | * @param src @param src_len message to encrypt 41 | * @param dest needs to point to a memory region at least @param src_len + 16 42 | * bytes big Returns written data size (msg payload plus sign data) 43 | */ 44 | int authenticate_and_encrypt(const uint64_t &nonce, const uint8_t *src, 45 | int src_len, uint8_t *dest); 46 | 47 | /** 48 | * For easy use - returns a buffer including (encrypted) payload plus 49 | * validation data 50 | */ 51 | std::shared_ptr> authenticate_and_encrypt_buff( 52 | const uint64_t &nonce, const uint8_t *src, std::size_t src_len); 53 | /** 54 | * Disables encryption (to save cpu performance) but keeps packet validation 55 | * functionality 56 | * @param encryption_enabled 57 | */ 58 | void set_encryption_enabled(bool encryption_enabled) { 59 | m_encrypt_data = encryption_enabled; 60 | } 61 | 62 | private: 63 | // tx->rx keypair 64 | const std::array tx_secretkey{}; 65 | const std::array rx_publickey{}; 66 | std::array session_key{}; 67 | // use this one if you are worried about CPU usage when using encryption 68 | bool m_encrypt_data = true; 69 | }; 70 | 71 | } // namespace wb 72 | 73 | #endif // ENCRIPTOR_HPP -------------------------------------------------------------------------------- /wifibroadcast/src/encryption/KeyPair.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by consti10 on 05.01.24. 3 | // 4 | #include "KeyPair.h" 5 | 6 | #include 7 | 8 | std::array wb::KeyPairTxRx::as_raw( 9 | const KeyPairTxRx& keypair) { 10 | std::array ret{}; 11 | std::memcpy(ret.data(), &keypair, KEYPAIR_RAW_SIZE); 12 | return ret; 13 | } 14 | 15 | wb::KeyPairTxRx wb::KeyPairTxRx::from_raw( 16 | const std::array& raw) { 17 | wb::KeyPairTxRx ret{}; 18 | std::memcpy(&ret, raw.data(), KEYPAIR_RAW_SIZE); 19 | return ret; 20 | } 21 | -------------------------------------------------------------------------------- /wifibroadcast/src/encryption/KeyPair.h: -------------------------------------------------------------------------------- 1 | #ifndef KEY_HPP 2 | #define KEY_HPP 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | namespace wb { 9 | // A wb key consists of a public and secret key 10 | struct Key { 11 | std::array public_key; 12 | std::array secret_key; 13 | int operator==(const Key& other) const { 14 | return std::equal(std::begin(public_key), std::end(public_key), 15 | std::begin(other.public_key)) && 16 | std::equal(std::begin(secret_key), std::end(secret_key), 17 | std::begin(other.secret_key)); 18 | } 19 | } __attribute__((packed)); 20 | ; 21 | static_assert(sizeof(Key) == 22 | crypto_box_PUBLICKEYBYTES + crypto_box_SECRETKEYBYTES); 23 | 24 | // A wb keypair are 2 keys, one for transmitting, one for receiving 25 | // (Since both ground and air unit talk bidirectional) 26 | // We use a different key for the down-link / uplink, respective 27 | static constexpr const int KEYPAIR_RAW_SIZE = 32 * 4; 28 | struct KeyPairTxRx { 29 | Key key_1; 30 | Key key_2; 31 | Key get_tx_key(bool is_air) { return is_air ? key_1 : key_2; } 32 | Key get_rx_key(bool is_air) { return is_air ? key_2 : key_1; } 33 | int operator==(const KeyPairTxRx& other) const { 34 | return key_1 == other.key_1 && key_2 == other.key_2; 35 | } 36 | static std::array as_raw( 37 | const KeyPairTxRx& keypair); 38 | static KeyPairTxRx from_raw(const std::array& raw); 39 | } __attribute__((packed)); 40 | static_assert(sizeof(KeyPairTxRx) == 2 * sizeof(Key)); 41 | static_assert(sizeof(KeyPairTxRx) == KEYPAIR_RAW_SIZE); 42 | 43 | } // namespace wb 44 | 45 | #endif // KEY_HPP 46 | -------------------------------------------------------------------------------- /wifibroadcast/src/fec/FEC.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by consti10 on 30.06.23. 3 | // 4 | 5 | #include "FEC.h" 6 | 7 | #include 8 | 9 | #include 10 | 11 | #include "../wifibroadcast_spdlog.h" 12 | #include "RxBlock.h" 13 | 14 | uint32_t calculate_n_secondary_fragments(uint32_t n_primary_fragments, 15 | uint32_t fec_overhead_perc) { 16 | if (fec_overhead_perc <= 0) return 0; 17 | const float n_secondary = static_cast(n_primary_fragments) * 18 | static_cast(fec_overhead_perc) / 100.0f; 19 | if (n_secondary <= 1.0) { 20 | // Always calculate at least one FEC packet 21 | return 1; 22 | } 23 | return std::lroundf(n_secondary); 24 | } 25 | 26 | unsigned int calculateN(const unsigned int k, const unsigned int percentage) { 27 | return k + calculate_n_secondary_fragments(k, percentage); 28 | } 29 | 30 | void fec_stream_print_fec_optimization_method() { print_optimization_method(); } 31 | -------------------------------------------------------------------------------- /wifibroadcast/src/fec/FECConstants.hpp: -------------------------------------------------------------------------------- 1 | #ifndef FEC_CONSTANTS_HPP 2 | #define FEC_CONSTANTS_HPP 3 | 4 | #include "FECPayloadHdr.hpp" 5 | 6 | static constexpr auto FRAGMENT_STATUS_UNAVAILABLE = false; 7 | static constexpr auto FRAGMENT_STATUS_AVAILABLE = true; 8 | 9 | // See WBTxRx 10 | static constexpr const auto MAX_PAYLOAD_BEFORE_FEC = 1449; 11 | // The FEC stream encode adds an overhead, leaving X bytes to the application 12 | static constexpr const auto FEC_PACKET_MAX_PAYLOAD_SIZE = 13 | MAX_PAYLOAD_BEFORE_FEC - sizeof(FECPayloadHdr); 14 | static_assert(FEC_PACKET_MAX_PAYLOAD_SIZE == 1441); 15 | 16 | // max 255 primary and secondary fragments together for now. Theoretically, this 17 | // implementation has enough bytes in the header for up to 15 bit fragment 18 | // indices, 2^15=32768 Note: currently limited by the fec c implementation 19 | static constexpr const uint16_t MAX_N_P_FRAGMENTS_PER_BLOCK = 128; 20 | static constexpr const uint16_t MAX_N_S_FRAGMENTS_PER_BLOCK = 128; 21 | static constexpr const uint16_t MAX_TOTAL_FRAGMENTS_PER_BLOCK = 22 | MAX_N_P_FRAGMENTS_PER_BLOCK + MAX_N_S_FRAGMENTS_PER_BLOCK; 23 | 24 | #endif // FEC_CONSTANTS_HPP 25 | -------------------------------------------------------------------------------- /wifibroadcast/src/fec/FECEncoder.h: -------------------------------------------------------------------------------- 1 | #ifndef FEC_ENCODER_HPP 2 | #define FEC_ENCODER_HPP 3 | 4 | #include 5 | #include 6 | 7 | #include "FECConstants.hpp" 8 | #include "TimeHelper.hpp" 9 | 10 | class FECEncoder { 11 | public: 12 | typedef std::function 13 | OUTPUT_DATA_CALLBACK; 14 | explicit FECEncoder(); 15 | FECEncoder(const FECEncoder& other) = delete; 16 | 17 | public: 18 | /** 19 | * Encodes a new block and forwards the packets for this block 20 | * forwards data packets first, then generated fec packets 21 | * (if needed) and forwards them after. 22 | * @param data_packets the packets for this block 23 | * @param n_secondary_fragments how many secondary fragments (FEC packets) 24 | * should be created 25 | */ 26 | void encode_block( 27 | const std::vector>>& data_packets, 28 | int n_secondary_fragments); 29 | /** 30 | * Distributes data evenly into @param n_primary_fragments and calculates 31 | * @param n_secondary_fragments afterwards. Reduces latency to a minimum by 32 | * forwarding packets via the cb as soon as possible (primary fragments are 33 | * forwarded before the fec step is performed). 34 | */ 35 | void fragment_and_encode(const uint8_t* data, int data_len, 36 | int n_primary_fragments, int n_secondary_fragments); 37 | OUTPUT_DATA_CALLBACK m_out_cb = nullptr; 38 | AvgCalculator m_fec_block_encode_time; 39 | MinMaxAvg m_curr_fec_block_encode_time{}; 40 | BaseAvgCalculator m_block_sizes{}; 41 | MinMaxAvg m_curr_fec_block_sizes{}; 42 | 43 | private: 44 | // Creates the (next) primary fragment 45 | // forward it via the data cb (such that it can be sent out as soon as 46 | // possible) and store the fragment for later use (fec_encode) 47 | void create_forward_save_fragment(const uint8_t* data, int data_len); 48 | // performs fec_encode (on the previously saved data) then forward all the fec 49 | // packets one after another. 50 | void create_fec_packets(int n_secondary_fragments); 51 | void init_block(int n_primary_fragments); 52 | // Pre-allocated to have space for storing primary fragments (they are needed 53 | // once the fec step needs to be performed) and creating the wanted amount of 54 | // secondary packets 55 | std::array, 56 | MAX_TOTAL_FRAGMENTS_PER_BLOCK> 57 | m_block_buffer{}; 58 | // Increased each time a block has been finished 59 | uint32_t m_curr_block_idx = 0; 60 | static_assert(sizeof(m_curr_block_idx) == sizeof(FECPayloadHdr::block_idx)); 61 | FECPayloadHdr m_fec_payload_hdr; 62 | int m_max_packet_size = -1; 63 | int m_fragment_index = 0; 64 | std::vector m_primary_fragments_data_p; 65 | }; 66 | 67 | #endif // FEC_ENCODER_HPP -------------------------------------------------------------------------------- /wifibroadcast/src/fec/FECPayloadHdr.hpp: -------------------------------------------------------------------------------- 1 | #ifndef FEC_PAYLOAD_HDR_HPP 2 | #define FEC_PAYLOAD_HDR_HPP 3 | 4 | #include 5 | 6 | #include "endian.h" 7 | /** 8 | * Encoder and Decoder pair for FEC protected block / packet based data 9 | * streaming. adds sizeof(FECPayloadHdr) to each fec primary or secondary 10 | * packet. 11 | */ 12 | 13 | static_assert(__BYTE_ORDER == __LITTLE_ENDIAN, 14 | "This code is written for little endian only !"); 15 | 16 | struct FECPayloadHdr { 17 | // Most often each frame is encoded as one fec block 18 | // rolling 19 | uint32_t block_idx = 0; 20 | // each fragment inside a block has a fragment index 21 | // uint8_t is enough, since we are limited to 128+128=256 fragments anyway by 22 | // the FEC impl. 23 | uint8_t fragment_idx = 0; 24 | // how many fragments make up the primary fragments part, the rest is 25 | // secondary fragments note that we do not need to know how many secondary 26 | // fragments have been created - as soon as we 'have enough', we can perform 27 | // the FEC correction step if necessary 28 | uint8_t n_primary_fragments = 0; 29 | // For FEC all data fragments have to be the same size. We pad the rest during 30 | // encoding / decoding with 0, and do this when encoding / decoding such that 31 | // the 0 bytes don't have to be transmitted. This needs to be included during 32 | // the fec encode / decode step ! 33 | uint16_t data_size = 0; 34 | } __attribute__((packed)); 35 | static_assert(sizeof(FECPayloadHdr) == 8); 36 | 37 | #endif // FEC_PAYLOAD_HDR_HPP -------------------------------------------------------------------------------- /wifibroadcast/src/legacy/README.md: -------------------------------------------------------------------------------- 1 | Usage of UDP is only still supported for demo purposes -------------------------------------------------------------------------------- /wifibroadcast/src/legacy/WBStreamRxUDP.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by consti10 on 02.07.23. 3 | // 4 | 5 | #ifndef WIFIBROADCAST_WBSTREAMRXUDP_H 6 | #define WIFIBROADCAST_WBSTREAMRXUDP_H 7 | 8 | #include "../WBStreamRx.h" 9 | #include "SocketHelper.hpp" 10 | 11 | /** 12 | * Uses UDP for data out instead of callback 13 | */ 14 | class WBStreamRxUDP { 15 | public: 16 | WBStreamRxUDP(std::shared_ptr txrx, WBStreamRx::Options options, 17 | int udp_port_out) { 18 | m_udp_out = std::make_unique( 19 | SocketHelper::ADDRESS_LOCALHOST, udp_port_out); 20 | wb_rx = std::make_unique(txrx, options); 21 | auto cb = [this](const uint8_t *payload, const std::size_t payloadSize) { 22 | // console->debug("Got data {}",payloadSize); 23 | m_udp_out->forwardPacketViaUDP(payload, payloadSize); 24 | }; 25 | wb_rx->set_callback(cb); 26 | auto console = wifibroadcast::log::create_or_get( 27 | fmt::format("radio_port{}->udp{}", options.radio_port, udp_port_out)); 28 | console->info("Sending data to localhost:{}", udp_port_out); 29 | } 30 | std::unique_ptr m_udp_out; 31 | std::unique_ptr wb_rx; 32 | 33 | private: 34 | }; 35 | 36 | #endif // WIFIBROADCAST_WBSTREAMRXUDP_H 37 | -------------------------------------------------------------------------------- /wifibroadcast/src/legacy/WBStreamTxUDP.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by consti10 on 02.07.23. 3 | // 4 | 5 | #ifndef WIFIBROADCAST_WBSTREAMTXUDP_H 6 | #define WIFIBROADCAST_WBSTREAMTXUDP_H 7 | 8 | #include "../WBStreamRx.h" 9 | #include "SocketHelper.hpp" 10 | 11 | /** 12 | * Uses UDP for data in instead of callback 13 | */ 14 | class WBStreamTxUDP { 15 | public: 16 | WBStreamTxUDP(std::shared_ptr txrx, WBStreamTx::Options options, 17 | int fec_k, int in_udp_port) { 18 | radiotap_header_holder = std::make_shared(); 19 | wb_tx = std::make_unique(txrx, options, radiotap_header_holder); 20 | last_udp_in_packet_ts_ms = MyTimeHelper::get_curr_time_ms(); 21 | // we need to buffer packets due to udp 22 | std::vector>> block; 23 | auto cb_udp_in = [this, &options, &block, &fec_k]( 24 | const uint8_t *payload, 25 | const std::size_t payloadSize) { 26 | last_udp_in_packet_ts_ms = MyTimeHelper::get_curr_time_ms(); 27 | if (options.enable_fec) { 28 | // We need to buffer data here for FEC 29 | auto packet = std::make_shared>( 30 | payload, payload + payloadSize); 31 | block.push_back(packet); 32 | if (block.size() == fec_k) { 33 | wb_tx->try_enqueue_block(block, 100, 20); 34 | block.resize(0); 35 | } 36 | } else { 37 | auto packet = std::make_shared>( 38 | payload, payload + payloadSize); 39 | wb_tx->try_enqueue_packet(packet); 40 | } 41 | }; 42 | m_udp_in = std::make_unique( 43 | SocketHelper::ADDRESS_LOCALHOST, in_udp_port, cb_udp_in); 44 | m_udp_in->runInBackground(); 45 | auto console = wifibroadcast::log::create_or_get( 46 | fmt::format("udp{}->radio_port{}", in_udp_port, options.radio_port)); 47 | console->info("Expecting data on localhost:{}", in_udp_port); 48 | if (options.enable_fec) { 49 | console->warn("This buffers {} packets on udp in !", fec_k); 50 | } 51 | } 52 | std::unique_ptr wb_tx; 53 | std::shared_ptr radiotap_header_holder; 54 | std::unique_ptr m_udp_in; 55 | // helps to catch a common newbie mistake of forgetting that this buffers in 56 | // packets 57 | int last_udp_in_packet_ts_ms; 58 | 59 | private: 60 | }; 61 | 62 | #endif // WIFIBROADCAST_WBSTREAMTXUDP_H 63 | -------------------------------------------------------------------------------- /wifibroadcast/src/radiotap/README.md: -------------------------------------------------------------------------------- 1 | Code for dealing with creating radiotap header for injection / 2 | parsing rx radiotap header (RF metrics). -------------------------------------------------------------------------------- /wifibroadcast/src/radiotap/RSSIAccumulator.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by consti10 on 30.06.23. 3 | // 4 | 5 | #ifndef WIFIBROADCAST_RSSIACCUMULATOR_HPP 6 | #define WIFIBROADCAST_RSSIACCUMULATOR_HPP 7 | 8 | #include 9 | 10 | #include "../wifibroadcast_spdlog.h" 11 | #include "TimeHelper.hpp" 12 | #include "spdlog/spdlog.h" 13 | 14 | /** 15 | * UINT16SeqNrHelper to accumulate RSSI values 16 | */ 17 | class RSSIAccumulator { 18 | public: 19 | void add_rssi(int8_t rssi) { 20 | if (rssi <= INT8_MIN || rssi >= 0) { 21 | // RSSI should always be negative and in range [-127,-1] 22 | // It seems to be quite common for drivers to report invalid rssi values 23 | // from time to time - in this case, just ignore the value 24 | if (m_debug_invalid_rssi) { 25 | wifibroadcast::log::get_default()->debug("Invalid rssi on id {}, {}", 26 | m_rssi_identifier, rssi); 27 | } 28 | return; 29 | } 30 | if (rssi > m_rssi_max) { 31 | m_rssi_max = rssi; 32 | } 33 | if (rssi < m_rssi_min) { 34 | m_rssi_min = rssi; 35 | } 36 | m_rssi_sum += static_cast(rssi); 37 | m_rssi_count++; 38 | } 39 | int8_t get_avg() const { 40 | const auto count = m_rssi_count; 41 | if (count <= 0) return INT8_MIN; 42 | const auto avg = m_rssi_sum / m_rssi_count; 43 | return static_cast(avg); 44 | } 45 | int8_t get_min() const { 46 | if (m_rssi_count <= 0) return INT8_MIN; 47 | return m_rssi_min; 48 | } 49 | int8_t get_max() const { 50 | if (m_rssi_count <= 0) return INT8_MIN; 51 | return m_rssi_max; 52 | } 53 | MinMaxAvg get_min_max_avg() { 54 | MinMaxAvg tmp{get_min(), get_max(), get_avg()}; 55 | return tmp; 56 | } 57 | static std::string min_max_avg_to_string(const MinMaxAvg& data, 58 | bool avg_only = false) { 59 | // Need to convert to int such that it is shown correctly 60 | MinMaxAvg tmp{data.min, data.max, data.avg}; 61 | return min_max_avg_as_string(tmp, avg_only); 62 | } 63 | int get_n_samples() { return m_rssi_count; } 64 | std::optional> add_and_recalculate_if_needed(int8_t rssi) { 65 | add_rssi(rssi); 66 | // Calculate every 20 packets or 500ms and at least one packet, whatever is 67 | // reached first 68 | const auto elapsed = 69 | std::chrono::steady_clock::now() - m_last_recalculation; 70 | if (get_n_samples() >= 20 || 71 | (get_n_samples() >= 1 && elapsed >= std::chrono::milliseconds(500))) { 72 | auto tmp = get_min_max_avg(); 73 | reset(); 74 | m_last_recalculation = std::chrono::steady_clock::now(); 75 | return tmp; 76 | } 77 | return std::nullopt; 78 | } 79 | void reset() { 80 | m_rssi_sum = 0; 81 | m_rssi_count = 0; 82 | m_rssi_min = INT8_MAX; 83 | m_rssi_max = INT8_MIN; 84 | } 85 | void set_debug_invalid_rssi(bool enable, int rssi_identifier) { 86 | m_debug_invalid_rssi = enable; 87 | m_rssi_identifier = rssi_identifier; 88 | } 89 | 90 | private: 91 | int m_rssi_sum = 0; 92 | int m_rssi_count = 0; 93 | int8_t m_rssi_min = INT8_MAX; 94 | int8_t m_rssi_max = INT8_MIN; 95 | std::chrono::steady_clock::time_point m_last_recalculation = 96 | std::chrono::steady_clock::now(); 97 | bool m_debug_invalid_rssi = false; 98 | int m_rssi_identifier = 0; 99 | }; 100 | 101 | #endif // WIFIBROADCAST_RSSIACCUMULATOR_HPP 102 | -------------------------------------------------------------------------------- /wifibroadcast/src/radiotap/RadiotapHeaderTxHolder.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by consti10 on 13.09.23. 3 | // 4 | 5 | #ifndef WIFIBROADCAST_RADIOTAPHEADERHOLDER_H 6 | #define WIFIBROADCAST_RADIOTAPHEADERHOLDER_H 7 | 8 | #include 9 | 10 | #include "../wifibroadcast_spdlog.h" 11 | #include "RadiotapHeaderTx.hpp" 12 | 13 | /** 14 | * Thread-safe holder for a (TX) radiotap header. 15 | * ( getter / setter) - 16 | * We modify the tx radiotap header in openhd at run time. 17 | * This kind of "atomic behaviour" is enough for openhd wifibroadcast. 18 | * TODO: Use std::atomic instead of std::mutex 19 | */ 20 | class RadiotapHeaderTxHolder { 21 | public: 22 | explicit RadiotapHeaderTxHolder() { 23 | m_console = wifibroadcast::log::get_default(); 24 | } 25 | void thread_safe_set(RadiotapHeaderTx::UserSelectableParams params) { 26 | m_radioTapHeaderParams = params; 27 | update_locked(); 28 | } 29 | RadiotapHeaderTx thread_safe_get() { 30 | std::lock_guard guard(m_radiotap_header_mutex); 31 | return m_radiotap_header; 32 | } 33 | 34 | public: 35 | void update_mcs_index(uint8_t mcs_index) { 36 | m_console->debug("update_mcs_index {}", mcs_index); 37 | m_radioTapHeaderParams.mcs_index = mcs_index; 38 | update_locked(); 39 | } 40 | void update_channel_width(int width_mhz) { 41 | m_console->debug("update_channel_width {}", width_mhz); 42 | m_radioTapHeaderParams.bandwidth = width_mhz; 43 | update_locked(); 44 | } 45 | void update_stbc(int stbc) { 46 | m_console->debug("update_stbc {}", stbc); 47 | if (stbc < 0 || stbc > 3) { 48 | m_console->warn("Invalid stbc index"); 49 | return; 50 | } 51 | m_radioTapHeaderParams.stbc = stbc; 52 | update_locked(); 53 | } 54 | void update_guard_interval(bool short_gi) { 55 | m_radioTapHeaderParams.short_gi = short_gi; 56 | update_locked(); 57 | } 58 | void update_ldpc(bool ldpc) { 59 | m_radioTapHeaderParams.ldpc = ldpc; 60 | update_locked(); 61 | } 62 | void update_set_flag_tx_no_ack(bool enable) { 63 | m_radioTapHeaderParams.set_flag_tx_no_ack = enable; 64 | update_locked(); 65 | } 66 | 67 | private: 68 | void update_locked() { 69 | auto header = RadiotapHeaderTx{m_radioTapHeaderParams}; 70 | // Swap out the actual header (thread-safe) 71 | std::lock_guard guard(m_radiotap_header_mutex); 72 | m_radiotap_header = header; 73 | } 74 | 75 | private: 76 | std::shared_ptr m_console; 77 | RadiotapHeaderTx::UserSelectableParams m_radioTapHeaderParams{}; 78 | RadiotapHeaderTx m_radiotap_header{RadiotapHeaderTx::UserSelectableParams{}}; 79 | std::mutex m_radiotap_header_mutex; 80 | }; 81 | 82 | #endif // WIFIBROADCAST_RADIOTAPHEADERHOLDER_H 83 | -------------------------------------------------------------------------------- /wifibroadcast/src/radiotap/RadiotapRxRfAggregator.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by consti10 on 05.10.23. 3 | // 4 | 5 | #include "RadiotapRxRfAggregator.h" 6 | 7 | void RadiotapRxRfAggregator::add_if_valid( 8 | const radiotap::rx::KeyRfIndicators& indicators, 9 | RadiotapRxRfAggregator::KeyRfAggregators& agg, 10 | RadiotapRxRfAggregator::AggKeyRfIndicators& curr) { 11 | if (indicators.radiotap_dbm_antsignal.has_value()) { 12 | const auto radiotap_dbm_antsignal = 13 | indicators.radiotap_dbm_antsignal.value(); 14 | auto opt_minmaxavg = 15 | agg.rssi_dbm.add_and_recalculate_if_needed(radiotap_dbm_antsignal); 16 | if (opt_minmaxavg.has_value()) { 17 | curr.rssi_dbm = opt_minmaxavg->avg; 18 | } 19 | } 20 | if (indicators.radiotap_dbm_antnoise.has_value()) { 21 | const auto radiotap_dbm_antnoise = indicators.radiotap_dbm_antnoise.value(); 22 | auto opt_minmaxavg = 23 | agg.noise_dbm.add_and_recalculate_if_needed(radiotap_dbm_antnoise); 24 | if (opt_minmaxavg.has_value()) { 25 | curr.noise_dbm = opt_minmaxavg->avg; 26 | } 27 | } 28 | if (indicators.radiotap_lock_quality.has_value()) { 29 | const auto radiotap_lock_quality = indicators.radiotap_lock_quality.value(); 30 | agg.signal_quality.add_signal_quality(radiotap_lock_quality); 31 | curr.card_signal_quality_perc = 32 | agg.signal_quality.get_current_signal_quality(); 33 | } 34 | } 35 | 36 | void RadiotapRxRfAggregator::on_valid_openhd_packet( 37 | const radiotap::rx::ParsedRxRadiotapPacket& packet) { 38 | add_if_valid(packet.rf_adapter, m_agg_adapter, m_current_rx_stats.adapter); 39 | for (int i = 0; i < packet.rf_paths.size() && i < 2; i++) { 40 | const auto& rf_path = packet.rf_paths[i]; 41 | auto& agg = i == 0 ? m_agg_antenna1 : m_agg_antenna2; 42 | auto& curr = 43 | i == 0 ? m_current_rx_stats.antenna1 : m_current_rx_stats.antenna2; 44 | add_if_valid(rf_path, agg, curr); 45 | } 46 | // if(m_wifi_cards[wlan_idx].type==wifibroadcast::WIFI_CARD_TYPE_RTL8812AU){ 47 | // RTL8812AU BUG - general value cannot be used, use max of antennas instead 48 | // this_wifi_card_stats.card_dbm=std::max(this_wifi_card_stats.antenna1_dbm,this_wifi_card_stats.antenna2_dbm); 49 | // }*/ 50 | } 51 | 52 | void RadiotapRxRfAggregator::set_debug_invalid_values(bool enable) { 53 | m_agg_adapter.rssi_dbm.set_debug_invalid_rssi(enable, 0); 54 | m_agg_adapter.noise_dbm.set_debug_invalid_rssi(enable, 0); 55 | m_agg_adapter.signal_quality.set_debug_invalid_signal_quality(enable); 56 | } 57 | 58 | void RadiotapRxRfAggregator::reset() { 59 | m_current_rx_stats = {}; 60 | m_agg_adapter.reset(); 61 | m_agg_antenna1.reset(); 62 | m_agg_antenna2.reset(); 63 | } 64 | 65 | std::string RadiotapRxRfAggregator::card_key_rf_indicators_to_string( 66 | const RadiotapRxRfAggregator::CardKeyRfIndicators& indicators) { 67 | return fmt::format( 68 | "RxRfStats[Adapter: {}:{}:{} | Antenna1: {}:{}:{} | Antenna2: {}:{}:{}]", 69 | indicators.adapter.rssi_dbm, indicators.adapter.noise_dbm, 70 | indicators.adapter.card_signal_quality_perc, indicators.antenna1.rssi_dbm, 71 | indicators.antenna1.noise_dbm, 72 | indicators.antenna1.card_signal_quality_perc, 73 | indicators.antenna2.rssi_dbm, indicators.antenna2.noise_dbm, 74 | indicators.antenna2.card_signal_quality_perc); 75 | } 76 | 77 | void RadiotapRxRfAggregator::debug_every_one_second() { 78 | const auto now = std::chrono::steady_clock::now(); 79 | if (now - m_last_debug_log >= std::chrono::seconds(1)) { 80 | auto current = get_current(); 81 | wifibroadcast::log::get_default()->debug( 82 | "{}", 83 | RadiotapRxRfAggregator::card_key_rf_indicators_to_string(current)); 84 | m_last_debug_log = now; 85 | } 86 | } 87 | 88 | void RadiotapRxRfAggregator::KeyRfAggregators::reset() { 89 | rssi_dbm.reset(); 90 | noise_dbm.reset(); 91 | signal_quality.reset(); 92 | } 93 | -------------------------------------------------------------------------------- /wifibroadcast/src/radiotap/RadiotapRxRfAggregator.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by consti10 on 05.10.23. 3 | // 4 | 5 | #ifndef WIFIBROADCAST_RADIOTAPRXRFAGGREGATOR_H 6 | #define WIFIBROADCAST_RADIOTAPRXRFAGGREGATOR_H 7 | 8 | #include 9 | 10 | #include "RSSIAccumulator.hpp" 11 | #include "RadiotapHeaderRx.hpp" 12 | #include "SignalQualityAccumulator.hpp" 13 | 14 | /** 15 | * Aggregates all key rf metrics. 16 | */ 17 | class RadiotapRxRfAggregator { 18 | public: 19 | // Aggregated (average) key rf metrics 20 | struct AggKeyRfIndicators { 21 | // -128 = invalid, [-127..-1] otherwise 22 | int8_t rssi_dbm = -128; 23 | int8_t noise_dbm = -128; 24 | // [0,100] if valid, -1 otherwise 25 | int8_t card_signal_quality_perc = -1; 26 | }; 27 | struct CardKeyRfIndicators { 28 | // ------------- PER ADAPTER ------------ 29 | AggKeyRfIndicators adapter; 30 | // -------------- PER ANTENNA ---------- 31 | AggKeyRfIndicators antenna1; 32 | AggKeyRfIndicators antenna2; 33 | }; 34 | // Called every time a valid openhd packet is received 35 | void on_valid_openhd_packet( 36 | const radiotap::rx::ParsedRxRadiotapPacket& packet); 37 | // debugging of the 'invalid values reported by driver' issue 38 | void set_debug_invalid_values(bool enable); 39 | // Reset all rf metrics 40 | void reset(); 41 | // TODO: Thread safety ? 42 | CardKeyRfIndicators get_current() { return m_current_rx_stats; } 43 | static std::string card_key_rf_indicators_to_string( 44 | const CardKeyRfIndicators& indicators); 45 | void debug_every_one_second(); 46 | 47 | private: 48 | struct KeyRfAggregators { 49 | RSSIAccumulator rssi_dbm; 50 | RSSIAccumulator noise_dbm; 51 | SignalQualityAccumulator signal_quality; 52 | void reset(); 53 | }; 54 | static void add_if_valid(const radiotap::rx::KeyRfIndicators& indicators, 55 | RadiotapRxRfAggregator::KeyRfAggregators& agg, 56 | RadiotapRxRfAggregator::AggKeyRfIndicators& curr); 57 | KeyRfAggregators m_agg_adapter; 58 | KeyRfAggregators m_agg_antenna1; 59 | KeyRfAggregators m_agg_antenna2; 60 | CardKeyRfIndicators m_current_rx_stats{}; 61 | std::chrono::steady_clock::time_point m_last_debug_log = 62 | std::chrono::steady_clock::now(); 63 | }; 64 | 65 | static std::ostream& operator<<( 66 | std::ostream& strm, 67 | const RadiotapRxRfAggregator::CardKeyRfIndicators& data) { 68 | strm << RadiotapRxRfAggregator::card_key_rf_indicators_to_string(data); 69 | return strm; 70 | } 71 | 72 | #endif // WIFIBROADCAST_RADIOTAPRXRFAGGREGATOR_H 73 | -------------------------------------------------------------------------------- /wifibroadcast/src/radiotap/SignalQualityAccumulator.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by consti10 on 09.08.23. 3 | // 4 | 5 | #ifndef WIFIBROADCAST_SIGNALQUALITYACCUMULATOR_HPP 6 | #define WIFIBROADCAST_SIGNALQUALITYACCUMULATOR_HPP 7 | 8 | #include 9 | 10 | #include "../wifibroadcast_spdlog.h" 11 | #include "TimeHelper.hpp" 12 | 13 | /** 14 | * Helper to accumulate (rtl8812au) signal quality values - 15 | * aka values that should always be in [0..100] range. 16 | */ 17 | class SignalQualityAccumulator { 18 | public: 19 | void add_signal_quality(int signal_quality_perc) { 20 | if (signal_quality_perc > 100 || signal_quality_perc < 0) { 21 | if (m_debug_invalid_signal_quality) { 22 | wifibroadcast::log::get_default()->debug("Invalid signal quality {}", 23 | signal_quality_perc); 24 | } 25 | return; 26 | } 27 | m_acc.add(signal_quality_perc); 28 | if (m_acc.getNSamples() > 10 || 29 | m_acc.get_delta_since_last_reset() > std::chrono::milliseconds(500)) { 30 | const auto tmp = m_acc.getMinMaxAvg(); 31 | const auto avg = tmp.avg; 32 | if (avg >= 0 && avg <= 100) { 33 | m_curr_signal_quality = avg; 34 | } 35 | m_acc.reset(); 36 | } 37 | } 38 | void reset() { 39 | m_acc.reset(); 40 | m_curr_signal_quality = -1; 41 | } 42 | int8_t get_current_signal_quality() const { return m_curr_signal_quality; } 43 | void set_debug_invalid_signal_quality(bool enable) { 44 | m_debug_invalid_signal_quality = enable; 45 | } 46 | 47 | private: 48 | BaseAvgCalculator m_acc; 49 | // -1 if invalid, [0,100] otherwise 50 | int8_t m_curr_signal_quality = -1; 51 | bool m_debug_invalid_signal_quality = false; 52 | }; 53 | 54 | #endif // WIFIBROADCAST_SIGNALQUALITYACCUMULATOR_HPP 55 | -------------------------------------------------------------------------------- /wifibroadcast/src/raw_socket_helper.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by consti10 on 16.08.23. 3 | // 4 | 5 | #ifndef WIFIBROADCAST_RAW_SOCKET_HELPER_HPP 6 | #define WIFIBROADCAST_RAW_SOCKET_HELPER_HPP 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include "SocketHelper.hpp" 13 | #include "wifibroadcast_spdlog.h" 14 | 15 | // taken from 16 | // https://github.com/OpenHD/Open.HD/blob/2.0/wifibroadcast-base/tx_rawsock.c#L86 17 | // open wifi interface using a socket (somehow this works ?!) 18 | static int open_wifi_interface_as_raw_socket(const std::string &wifi) { 19 | auto console = wifibroadcast::log::create_or_get("raw_sock"); 20 | struct sockaddr_ll ll_addr {}; 21 | struct ifreq ifr {}; 22 | int sock = socket(AF_PACKET, SOCK_RAW, 0); 23 | if (sock == -1) { 24 | console->error("open socket failed {} {}", wifi.c_str(), strerror(errno)); 25 | } 26 | 27 | ll_addr.sll_family = AF_PACKET; 28 | ll_addr.sll_protocol = 0; 29 | ll_addr.sll_halen = ETH_ALEN; 30 | 31 | strncpy(ifr.ifr_name, wifi.c_str(), IFNAMSIZ); 32 | 33 | if (ioctl(sock, SIOCGIFINDEX, &ifr) < 0) { 34 | console->error("ioctl(SIOCGIFINDEX) failed"); 35 | } 36 | 37 | ll_addr.sll_ifindex = ifr.ifr_ifindex; 38 | 39 | if (ioctl(sock, SIOCGIFHWADDR, &ifr) < 0) { 40 | console->error("ioctl(SIOCGIFHWADDR) failed"); 41 | } 42 | 43 | memcpy(ll_addr.sll_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); 44 | 45 | if (bind(sock, (struct sockaddr *)&ll_addr, sizeof(ll_addr)) == -1) { 46 | close(sock); 47 | console->error("bind failed"); 48 | } 49 | SocketHelper::debug_send_rcv_timeout(sock, console); 50 | // const auto wanted_send_timeout=std::chrono::milliseconds(20); 51 | // console->debug("Setting send timeout to 52 | // {}",MyTimeHelper::R(wanted_send_timeout)); 53 | // SocketHelper::set_socket_send_rcv_timeout(sock,std::chrono::milliseconds(20), 54 | // true); 55 | // debug the timeout after setting 56 | // SocketHelper::debug_send_rcv_timeout(sock,console); 57 | // buff size 58 | SocketHelper::set_socket_send_rcv_buffsize(sock, 1510 * 1, true); 59 | SocketHelper::debug_send_rcv_buffsize(sock, console); 60 | // const int wanted_sendbuff_bytes=128*1024*1024; 61 | // SocketHelper::set_socket_send_rcv_buffsize(sock,wanted_sendbuff_bytes, 62 | // true); 63 | // buff size end 64 | console->debug("{} socket opened", wifi); 65 | return sock; 66 | } 67 | 68 | #endif // WIFIBROADCAST_RAW_SOCKET_HELPER_HPP 69 | -------------------------------------------------------------------------------- /wifibroadcast/src/tmp.txt: -------------------------------------------------------------------------------- 1 | // regarding the broadcast stuff 2 | // ( a lot of contradictary info) 3 | https://gist.github.com/Lothiraldan/3951784 4 | 5 | if(addGroup){ 6 | auto GROUP="239.255.255.250"; 7 | //auto GROUP="127.0.0.1"; 8 | struct ip_mreq mreq; 9 | mreq.imr_multiaddr.s_addr = inet_addr(GROUP); 10 | mreq.imr_interface.s_addr = htonl(INADDR_ANY); 11 | if ( 12 | setsockopt( 13 | mSocket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*) &mreq, sizeof(mreq) 14 | ) < 0 15 | ){ 16 | perror("setsockopt"); 17 | return; 18 | } 19 | }else{ 20 | auto GROUP="239.255.255.251"; 21 | //auto GROUP="127.0.0.1"; 22 | struct ip_mreq mreq; 23 | mreq.imr_multiaddr.s_addr = inet_addr(GROUP); 24 | mreq.imr_interface.s_addr = htonl(INADDR_ANY); 25 | if ( 26 | setsockopt( 27 | mSocket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*) &mreq, sizeof(mreq) 28 | ) < 0 29 | ){ 30 | perror("setsockopt"); 31 | return; 32 | } 33 | } 34 | 35 | static int openUdpSocketForSendingData2(const std::string &client_addr, int client_port) { 36 | struct sockaddr_in saddr; 37 | int fd = socket(AF_INET, SOCK_DGRAM, 0); 38 | if (fd < 0) throw std::runtime_error(StringFormat::convert("Error opening socket: %s", strerror(errno))); 39 | 40 | bzero((char *) &saddr, sizeof(saddr)); 41 | saddr.sin_family = AF_INET; 42 | saddr.sin_addr.s_addr = inet_addr(client_addr.c_str()); 43 | saddr.sin_port = htons((unsigned short) client_port); 44 | 45 | if (connect(fd, (struct sockaddr *) &saddr, sizeof(saddr)) < 0) { 46 | throw std::runtime_error(StringFormat::convert("Connect error: %s", strerror(errno))); 47 | } 48 | 49 | // multicast 50 | int enable = 1; 51 | if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0){ 52 | throw std::runtime_error(StringFormat::convert("Error setting reuse on socket %d: %s",client_port, strerror(errno))); 53 | } 54 | if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &enable, sizeof(int)) < 0){ 55 | throw std::runtime_error(StringFormat::convert("Error setting multicast on socket %d: %s",client_port, strerror(errno))); 56 | } 57 | // 58 | 59 | 60 | return fd; 61 | } 62 | -------------------------------------------------------------------------------- /wifibroadcast/src/wifibroadcast_spdlog.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by consti10 on 13.08.23. 3 | // 4 | #include "wifibroadcast_spdlog.h" 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | std::shared_ptr wifibroadcast::log::create_or_get( 13 | const std::string& logger_name) { 14 | static std::mutex logger_mutex2{}; 15 | std::lock_guard guard(logger_mutex2); 16 | auto ret = spdlog::get(logger_name); 17 | if (ret == nullptr) { 18 | auto created = spdlog::stdout_color_mt(logger_name); 19 | 20 | std::ifstream file("/usr/share/openhd/debug.txt"); 21 | if (file.good()) { 22 | created->set_level(spdlog::level::debug); 23 | } else { 24 | created->set_level(spdlog::level::warn); 25 | } 26 | 27 | assert(created); 28 | return created; 29 | } 30 | return ret; 31 | } 32 | 33 | std::shared_ptr wifibroadcast::log::get_default() { 34 | return create_or_get("wifibroadcast"); 35 | } 36 | -------------------------------------------------------------------------------- /wifibroadcast/src/wifibroadcast_spdlog.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by consti10 on 14.11.22. 3 | // 4 | 5 | #ifndef WIFIBROADCAST_SRC_WIFIBROADCAST_SPDLOG_H_ 6 | #define WIFIBROADCAST_SRC_WIFIBROADCAST_SPDLOG_H_ 7 | 8 | #include 9 | 10 | #include 11 | 12 | namespace wifibroadcast::log { 13 | 14 | std::shared_ptr create_or_get(const std::string& logger_name); 15 | 16 | std::shared_ptr get_default(); 17 | 18 | } // namespace wifibroadcast::log 19 | #endif // WIFIBROADCAST_SRC_WIFIBROADCAST_SPDLOG_H_ 20 | --------------------------------------------------------------------------------