├── arrows.png ├── pics ├── dickie.jpg ├── rover.jpg ├── concept.png ├── ook-car.jpg └── inspectrum-ook.png ├── include ├── ui.h └── rf.h ├── .gitignore ├── .github └── workflows │ └── build.yml ├── src ├── rover.cpp ├── dickie.cpp ├── concept.cpp ├── ook_car.cpp ├── ui.cpp └── rf.cpp ├── Makefile ├── LICENSE └── README.md /arrows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rgerganov/rf-car/HEAD/arrows.png -------------------------------------------------------------------------------- /pics/dickie.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rgerganov/rf-car/HEAD/pics/dickie.jpg -------------------------------------------------------------------------------- /pics/rover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rgerganov/rf-car/HEAD/pics/rover.jpg -------------------------------------------------------------------------------- /pics/concept.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rgerganov/rf-car/HEAD/pics/concept.png -------------------------------------------------------------------------------- /pics/ook-car.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rgerganov/rf-car/HEAD/pics/ook-car.jpg -------------------------------------------------------------------------------- /pics/inspectrum-ook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rgerganov/rf-car/HEAD/pics/inspectrum-ook.png -------------------------------------------------------------------------------- /include/ui.h: -------------------------------------------------------------------------------- 1 | #ifndef __UI_H__ 2 | #define __UI_H__ 3 | 4 | class RfCar; 5 | int RenderUI(RfCar *car); 6 | 7 | #endif -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Compiled Dynamic libraries 12 | *.so 13 | *.dylib 14 | *.dll 15 | 16 | # Fortran module files 17 | *.mod 18 | *.smod 19 | 20 | # Compiled Static libraries 21 | *.lai 22 | *.la 23 | *.a 24 | *.lib 25 | 26 | # Executables 27 | *.exe 28 | *.out 29 | *.app 30 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | ubuntu-build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Dependencies 17 | run: | 18 | sudo apt update 19 | sudo apt install libsdl2-dev libsdl2-image-dev libhackrf-dev 20 | - name: Build 21 | run: make 22 | 23 | osx-build: 24 | 25 | runs-on: macos-latest 26 | 27 | steps: 28 | - uses: actions/checkout@v2 29 | - name: Dependencies 30 | run: | 31 | brew update 32 | brew install sdl2 sdl2_image hackrf 33 | - name: Build 34 | run: make 35 | -------------------------------------------------------------------------------- /src/rover.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "rf.h" 5 | #include "ui.h" 6 | 7 | int main(int argc, char *argv[]) 8 | { 9 | char optc; 10 | bool always_tx = false; 11 | 12 | while ((optc = getopt(argc, argv, "X")) > 0) { 13 | switch (optc) { 14 | case 'X': 15 | always_tx = true; 16 | break; 17 | default: 18 | fprintf(stderr, "%s [args]\n", argv[0]); 19 | fprintf(stderr, "\t-X\t\talways TX\n"); 20 | exit(EXIT_FAILURE); 21 | } 22 | } 23 | 24 | RangeRover car(2416000000, 10000000, 1000000, always_tx); 25 | if (!car.init()) { 26 | return 1; 27 | } 28 | return RenderUI(&car); 29 | } 30 | -------------------------------------------------------------------------------- /src/dickie.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "rf.h" 5 | #include "ui.h" 6 | 7 | int main(int argc, char *argv[]) 8 | { 9 | char optc; 10 | bool always_tx = false; 11 | 12 | while ((optc = getopt(argc, argv, "X")) > 0) { 13 | switch (optc) { 14 | case 'X': 15 | always_tx = true; 16 | break; 17 | default: 18 | fprintf(stderr, "%s [args]\n", argv[0]); 19 | fprintf(stderr, "\t-X\t\talways TX\n"); 20 | exit(EXIT_FAILURE); 21 | } 22 | } 23 | 24 | Dickie car(2444000000, 10000000, 1000000, always_tx); 25 | if (!car.init()) { 26 | return 1; 27 | } 28 | car.sendSync(); 29 | return RenderUI(&car); 30 | } 31 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SRC_DIR := src 2 | 3 | SRC := $(wildcard $(SRC_DIR)/*.cpp) 4 | OBJ := $(SRC:$(SRC_DIR)/%.cpp=$(SRC_DIR)/%.o) 5 | 6 | CXXFLAGS = -Wall -std=c++11 -Iinclude $(shell pkg-config --cflags libhackrf) $(shell pkg-config --cflags sdl2) 7 | LDFLAGS = $(shell pkg-config --libs sdl2) -lSDL2_image $(shell pkg-config --libs libhackrf) 8 | 9 | .PHONY: all clean 10 | 11 | all: ook_car dickie concept rover 12 | 13 | ook_car: src/ook_car.o src/rf.o src/ui.o 14 | $(CXX) $^ -o $@ $(LDFLAGS) 15 | 16 | dickie: src/dickie.o src/rf.o src/ui.o 17 | $(CXX) $^ -o $@ $(LDFLAGS) 18 | 19 | concept: src/concept.o src/rf.o src/ui.o 20 | $(CXX) $^ -o $@ $(LDFLAGS) 21 | 22 | rover: src/rover.o src/rf.o src/ui.o 23 | $(CXX) $^ -o $@ $(LDFLAGS) 24 | 25 | $(SRC_DIR)/%.o: $(SRC_DIR)/%.cpp 26 | $(CXX) $(CXXFLAGS) -c $< -o $@ 27 | 28 | clean: 29 | rm -f ook_car dickie concept rover $(OBJ) 30 | -------------------------------------------------------------------------------- /src/concept.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "rf.h" 5 | #include "ui.h" 6 | 7 | int main(int argc, char *argv[]) 8 | { 9 | char optc; 10 | bool always_tx = false; 11 | 12 | while ((optc = getopt(argc, argv, "X")) > 0) { 13 | switch (optc) { 14 | case 'X': 15 | always_tx = true; 16 | break; 17 | default: 18 | fprintf(stderr, "%s [args]\n", argv[0]); 19 | fprintf(stderr, "\t-X\t\talways TX\n"); 20 | exit(EXIT_FAILURE); 21 | } 22 | } 23 | 24 | //note: correction -1.25MHz is made because of offset for LOW and HIGH bits in FskCar::txCallback 25 | Concept car(2417000000 - 1250000, 10000000, 1000000, always_tx); 26 | if (!car.init()) { 27 | return 1; 28 | } 29 | car.sendSync(); 30 | return RenderUI(&car); 31 | } 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016-2022 Radoslav Gerganov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/ook_car.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "rf.h" 5 | #include "ui.h" 6 | 7 | int main(int argc, char *argv[]) 8 | { 9 | char optc; 10 | bool invert_steering = false; 11 | bool invert_throttle = false; 12 | bool always_tx = false; 13 | int64_t freq = 40684300; 14 | int sample_rate = 2000000; 15 | 16 | while ((optc = getopt(argc, argv, "f:s:STX")) > 0) { 17 | switch (optc) { 18 | case 'f': 19 | freq = atoi(optarg); 20 | break; 21 | case 's': 22 | sample_rate = atoi(optarg); 23 | break; 24 | case 'S': 25 | invert_steering = true; 26 | break; 27 | case 'T': 28 | invert_throttle = true; 29 | break; 30 | case 'X': 31 | always_tx = true; 32 | break; 33 | default: 34 | fprintf(stderr, "%s [args]\n", argv[0]); 35 | fprintf(stderr, "\t-f FREQUENCY\tset frequency (integer, HZ)\n"); 36 | fprintf(stderr, "\t-s SAMPLE_RATE\tset sample rate\n"); 37 | fprintf(stderr, "\t-S\t\tinvert steering\n"); 38 | fprintf(stderr, "\t-T\t\tinvert throttle\n"); 39 | fprintf(stderr, "\t-X\t\talways TX\n"); 40 | exit(EXIT_FAILURE); 41 | } 42 | } 43 | 44 | OokCar car(freq, sample_rate, 2018, always_tx); 45 | if (!car.init()) { 46 | return 1; 47 | } 48 | if (invert_steering) { 49 | car.invertSteering(); 50 | } 51 | if (invert_throttle) { 52 | car.invertThrottle(); 53 | } 54 | return RenderUI(&car); 55 | } 56 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | [![Build Status](https://github.com/rgerganov/rf-car/workflows/CI/badge.svg)](https://github.com/rgerganov/rf-car/actions) 3 | 4 | Small programs for controlling RC cars with HackRF. 5 | 6 | ## `ook_car` 7 | ![ook-car](/pics/ook-car.jpg) 8 | 9 | This car works on 40.684 MHz and moves in 8 directions (forward, backward, left, right, forward-right, 10 | forward-left, backward-right, backward-left). You can see it in action here: 11 | 12 | [![demo-ook](https://img.youtube.com/vi/itS2pWkgNrM/0.jpg)](https://www.youtube.com/watch?v=itS2pWkgNrM) 13 | 14 | The remote control is using OOK modulation with long and short pulses. One long 15 | pulse is equal to three short pulses. For example, to move the car forward, we 16 | need to send 4 long pulses followed by 10 short pulses. We can easily find the 17 | control sequence for each direction by recording the signal from the RC and 18 | then analyse it with [inspectrum](https://github.com/miek/inspectrum): 19 | 20 | ![ook-signal](/pics/inspectrum-ook.png) 21 | 22 | To synthesize the signal with the HackRF, we need to transmit 23 | `SAMPLE_RATE/SYMBOL_RATE` samples ('1' or '0') for each bit of the control 24 | sequence. We can find the `SYMBOL_RATE` with inspectrum, it is about 2018. 25 | We choose the `SAMPLE_RATE` to be 2M. 26 | 27 | ## `dickie` 28 | ![dickie-car](/pics/dickie.jpg) 29 | 30 | This is the [Dickie Toys Flippy RC Car](https://www.amazon.de/-/en/Dickie-Flippy-Control-Rotation-Function/dp/B084PY44PN), it works on 2.4 GHz and moves in 6 directions. You can see it in action here: 31 | 32 | [![demo-dickie](https://img.youtube.com/vi/mqSv-Nycy_4/0.jpg)](https://www.youtube.com/watch?v=mqSv-Nycy_4) 33 | 34 | First you need to turn on the car and then start `dickie`. This is because `dickie` sends a synchronization pattern upon start and the car needs to receive it. 35 | You can find more details about the remote protocol in this [blog post](https://xakcop.com/post/re-2.4ghz/). 36 | 37 | ## `concept` 38 | ![dickie-car](/pics/concept.png) 39 | 40 | This car is very similar to the one from Dickie Toys. The support was added by [@alexbilevskiy](https://github.com/alexbilevskiy), you can find more details in [this PR](https://github.com/rgerganov/rf-car/pull/4). 41 | 42 | ## `rover` 43 | ![rover-car](/pics/rover.jpg) 44 | 45 | This is Range Rover donated by [thegiftscholars.com](https://www.thegiftscholars.com/cmj-rc-cars) especially for this project! 46 | It works on 2.4 GHz and moves in 8 directions. You can see it in action here: 47 | 48 | [![demo-rover](https://img.youtube.com/vi/QaZhaHERiQc/0.jpg)](https://www.youtube.com/watch?v=QaZhaHERiQc) 49 | 50 | # Build & run 51 | 52 | The programs depend only on SDL2, SDL2_image and libhackrf. To build on Linux: 53 | 54 | $ sudo apt-get install libsdl2-dev libsdl2-image-dev libhackrf-dev 55 | $ make 56 | $ ./ook_car 57 | $ ./dickie 58 | $ ./rover 59 | 60 | To build on OSX: 61 | 62 | $ brew install sdl2 sdl2_image hackrf 63 | $ make 64 | $ ./ook_car 65 | $ ./dickie 66 | $ ./rover 67 | 68 | # Support 69 | 70 | If you want to support the project you can send me an RC toy and I will try to reverse engineer its protocol and add it here. Send me an [email](mailto:rgerganov@gmail.com) for more details. 71 | -------------------------------------------------------------------------------- /include/rf.h: -------------------------------------------------------------------------------- 1 | #ifndef __RF_H__ 2 | #define __RF_H__ 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | enum Direction 9 | { 10 | FWD, FWD_LEFT, FWD_RIGHT, BACK, BACK_LEFT, BACK_RIGHT, LEFT, RIGHT, NONE, SYNC, STOP 11 | }; 12 | 13 | struct hackrf_device; 14 | class RfCar 15 | { 16 | public: 17 | RfCar(uint64_t freq, int sample_rate, int symbol_rate, bool always_tx) : freq(freq), 18 | sample_rate(sample_rate), 19 | symbol_rate(symbol_rate), 20 | always_tx(always_tx) { 21 | } 22 | bool init(); 23 | void startTx(); 24 | void changeState(Direction dir, int gain_tx); 25 | void stopTx(); 26 | void close(); 27 | // return if the specified direction is supported by the implementation 28 | virtual bool supportDirection(Direction dir) = 0; 29 | // send specific pattern at the end of the transmission 30 | virtual void txEnd() = 0; 31 | virtual int txCallback(uint8_t* buffer, int valid_length) = 0; 32 | virtual ~RfCar() {} 33 | 34 | protected: 35 | uint64_t freq; 36 | int sample_rate; 37 | int symbol_rate; 38 | int last_gain_tx = 30; 39 | std::atomic last_dir{NONE}; 40 | hackrf_device *device; 41 | // current index in the bit pattern being transmitted 42 | // always divided by samples_per_bit and used modulo pattern size 43 | std::atomic pos{0}; 44 | // true if tx has been started 45 | bool tx_started = false; 46 | // always transmit, even if the user is not pressing anything 47 | // (the app is more responsive this way because start/stop tx takes time) 48 | bool always_tx; 49 | }; 50 | 51 | class FskCar : public RfCar 52 | { 53 | public: 54 | FskCar(uint64_t freq, int sample_rate, int symbol_rate, bool always_tx) : RfCar(freq, sample_rate, symbol_rate, always_tx) { 55 | } 56 | virtual void txEnd(); 57 | virtual int txCallback(uint8_t* buffer, int valid_length); 58 | virtual ~FskCar() {} 59 | 60 | protected: 61 | // use maps as sparse arrays mapping array index to array value 62 | std::unordered_map patterns[11]; 63 | int pattern_size = 0; 64 | int sync_pattern_size = 0; 65 | float phase = 0; 66 | }; 67 | 68 | class Dickie : public FskCar 69 | { 70 | public: 71 | Dickie(uint64_t freq, int sample_rate, int symbol_rate, bool always_tx); 72 | void sendSync(); 73 | virtual bool supportDirection(Direction dir) { 74 | return dir != LEFT && dir != RIGHT; 75 | } 76 | virtual ~Dickie() {} 77 | }; 78 | 79 | class Concept : public FskCar 80 | { 81 | public: 82 | Concept(uint64_t freq, int sample_rate, int symbol_rate, bool always_tx); 83 | void sendSync(); 84 | virtual bool supportDirection(Direction dir) { 85 | return dir != LEFT && dir != RIGHT; 86 | } 87 | virtual ~Concept() {} 88 | }; 89 | 90 | class RangeRover : public FskCar 91 | { 92 | public: 93 | RangeRover(uint64_t freq, int sample_rate, int symbol_rate, bool always_tx); 94 | virtual bool supportDirection(Direction dir) { 95 | return true; 96 | } 97 | virtual ~RangeRover() {} 98 | }; 99 | 100 | class OokCar : public RfCar 101 | { 102 | public: 103 | OokCar(uint64_t freq, int sample_rate, int symbol_rate, bool always_tx); 104 | virtual bool supportDirection(Direction dir) { 105 | return true; 106 | } 107 | virtual void txEnd() { 108 | // nop 109 | } 110 | void invertSteering(); 111 | void invertThrottle(); 112 | virtual int txCallback(uint8_t* buffer, int valid_length); 113 | virtual ~OokCar() {} 114 | 115 | private: 116 | std::vector patterns[11]; 117 | std::vector filter; 118 | }; 119 | #endif 120 | -------------------------------------------------------------------------------- /src/ui.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "ui.h" 5 | #include "rf.h" 6 | 7 | int RenderUI(RfCar *car) 8 | { 9 | if (SDL_Init(SDL_INIT_VIDEO) != 0) { 10 | fprintf(stderr, "SDL_Init Error: %s\n", SDL_GetError()); 11 | return 1; 12 | } 13 | SDL_Window *window = SDL_CreateWindow("rf-car", SDL_WINDOWPOS_CENTERED, 14 | SDL_WINDOWPOS_CENTERED, 640, 480, SDL_WINDOW_SHOWN); 15 | if (window == NULL) { 16 | fprintf(stderr, "CreateWindow Error: %s\n", SDL_GetError()); 17 | return 1; 18 | } 19 | SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); 20 | if (renderer == NULL) { 21 | fprintf(stderr, "CreateRenderer Error: %s\n", SDL_GetError()); 22 | return 1; 23 | } 24 | 25 | SDL_Texture *arrows = IMG_LoadTexture(renderer, "arrows.png"); 26 | if (arrows == NULL) { 27 | fprintf(stderr, "Cannot load arrows Error: %s\n", SDL_GetError()); 28 | return 1; 29 | } 30 | 31 | SDL_Event e; 32 | bool quit = false; 33 | SDL_Rect up_arr = {0, 0, 100, 100}; 34 | SDL_Rect down_arr = {100, 0, 100, 100}; 35 | SDL_Rect right_arr = {200, 0, 100, 100}; 36 | SDL_Rect left_arr = {300, 0, 100, 100}; 37 | SDL_Rect up_arr_p = {0, 100, 100, 100}; 38 | SDL_Rect down_arr_p = {100, 100, 100, 100}; 39 | SDL_Rect right_arr_p = {200, 100, 100, 100}; 40 | SDL_Rect left_arr_p = {300, 100, 100, 100}; 41 | 42 | int center_x = 320; 43 | int center_y = 270; 44 | SDL_Rect up_dst = {center_x-50, center_y-200, 100, 100}; 45 | SDL_Rect down_dst = {center_x-50, center_y+100, 100, 100}; 46 | SDL_Rect right_dst = {center_x+100, center_y-50, 100, 100}; 47 | SDL_Rect left_dst = {center_x-200, center_y-50, 100, 100}; 48 | int gain_tx = 20; 49 | 50 | while (!quit) { 51 | while (SDL_PollEvent(&e)) { 52 | if (e.type == SDL_QUIT) { 53 | quit = true; 54 | } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_ESCAPE) { 55 | quit = true; 56 | } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_EQUALS) { 57 | if (e.key.keysym.mod & KMOD_SHIFT) { 58 | gain_tx += 1; 59 | } 60 | } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_MINUS) { 61 | gain_tx -= 1; 62 | } else if (e.type == SDL_MOUSEWHEEL) { 63 | gain_tx += e.wheel.y; 64 | } 65 | } 66 | SDL_Rect *up_src = &up_arr; 67 | SDL_Rect *down_src = &down_arr; 68 | SDL_Rect *right_src = &right_arr; 69 | SDL_Rect *left_src = &left_arr; 70 | const Uint8 *state = SDL_GetKeyboardState(NULL); 71 | Direction dir = NONE; 72 | if (state[SDL_SCANCODE_UP]) { 73 | up_src = &up_arr_p; 74 | dir = FWD; 75 | if (state[SDL_SCANCODE_RIGHT]) { 76 | right_src = &right_arr_p; 77 | dir = FWD_RIGHT; 78 | } else if (state[SDL_SCANCODE_LEFT]) { 79 | left_src = &left_arr_p; 80 | dir = FWD_LEFT; 81 | } 82 | } else if (state[SDL_SCANCODE_DOWN]) { 83 | down_src = &down_arr_p; 84 | dir = BACK; 85 | if (state[SDL_SCANCODE_RIGHT]) { 86 | right_src = &right_arr_p; 87 | dir = BACK_RIGHT; 88 | } else if (state[SDL_SCANCODE_LEFT]) { 89 | left_src = &left_arr_p; 90 | dir = BACK_LEFT; 91 | } 92 | } else if (state[SDL_SCANCODE_RIGHT]) { 93 | right_src = &right_arr_p; 94 | dir = RIGHT; 95 | } else if (state[SDL_SCANCODE_LEFT]) { 96 | left_src = &left_arr_p; 97 | dir = LEFT; 98 | } 99 | 100 | gain_tx = (gain_tx > 0) ? gain_tx : 0; 101 | gain_tx = (gain_tx < 47) ? gain_tx : 47; 102 | 103 | car->changeState(dir, gain_tx); 104 | 105 | SDL_SetRenderDrawColor(renderer, 0xdc, 0xdc, 0xdc, 0xFF); 106 | SDL_RenderClear(renderer); 107 | SDL_RenderCopy(renderer, arrows, up_src, &up_dst); 108 | SDL_RenderCopy(renderer, arrows, down_src, &down_dst); 109 | SDL_RenderCopy(renderer, arrows, right_src, &right_dst); 110 | SDL_RenderCopy(renderer, arrows, left_src, &left_dst); 111 | SDL_Rect gain_border = {0, 0, 580, 40}; 112 | SDL_Rect gain_one = {10, 10, 10, 20}; 113 | SDL_SetRenderDrawColor(renderer, 0x70, 0x80, 0x90, 0xFF); 114 | SDL_RenderFillRect(renderer, &gain_border); 115 | gain_border.x += 5; 116 | gain_border.y += 5; 117 | gain_border.w -= 10; 118 | gain_border.h -= 10; 119 | SDL_SetRenderDrawColor(renderer, 0xdc, 0xdc, 0xdc, 0xFF); 120 | SDL_RenderFillRect(renderer, &gain_border); 121 | SDL_SetRenderDrawColor(renderer, 0xff, 0x00, 0x00, 0xFF); 122 | for (int i = 0 ; i < gain_tx ; i++) { 123 | SDL_RenderFillRect(renderer, &gain_one); 124 | gain_one.x += 12; 125 | } 126 | SDL_RenderPresent(renderer); 127 | } 128 | 129 | car->close(); 130 | SDL_DestroyRenderer(renderer); 131 | SDL_DestroyWindow(window); 132 | SDL_Quit(); 133 | return 0; 134 | } -------------------------------------------------------------------------------- /src/rf.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "rf.h" 9 | 10 | bool RfCar::init() 11 | { 12 | int result = hackrf_init(); 13 | if (result != HACKRF_SUCCESS) { 14 | fprintf(stderr, "hackrf_init() failed: (%d)\n", result); 15 | return false; 16 | } 17 | hackrf_device_list_t *list = hackrf_device_list(); 18 | if (list->devicecount < 1) { 19 | fprintf(stderr, "No HackRF boards found.\n"); 20 | return false; 21 | } 22 | hackrf_device_list_free(list); 23 | return true; 24 | } 25 | 26 | void RfCar::close() 27 | { 28 | if (always_tx) { 29 | always_tx = false; 30 | stopTx(); 31 | } 32 | hackrf_exit(); 33 | } 34 | 35 | static int tx_callback(hackrf_transfer* transfer) 36 | { 37 | RfCar *rfcar = (RfCar*) transfer->tx_ctx; 38 | return rfcar->txCallback(transfer->buffer, transfer->valid_length); 39 | } 40 | 41 | void RfCar::startTx() 42 | { 43 | if (always_tx && tx_started) { 44 | return; 45 | } 46 | int result = hackrf_open(&device); 47 | if (result != HACKRF_SUCCESS) { 48 | fprintf(stderr, "hackrf_open() failed: (%d)\n", result); 49 | } 50 | result = hackrf_set_sample_rate_manual(device, sample_rate, 1); 51 | if (result != HACKRF_SUCCESS) { 52 | fprintf(stderr, "hackrf_sample_rate_set() failed: (%d)\n", result); 53 | } 54 | uint32_t baseband_filter_bw_hz = hackrf_compute_baseband_filter_bw_round_down_lt(sample_rate); 55 | result = hackrf_set_baseband_filter_bandwidth(device, baseband_filter_bw_hz); 56 | if (result != HACKRF_SUCCESS) { 57 | fprintf(stderr, "hackrf_baseband_filter_bandwidth_set() failed: (%d)\n", result); 58 | } 59 | result = hackrf_set_freq(device, freq); 60 | if (result != HACKRF_SUCCESS) { 61 | fprintf(stderr, "hackrf_set_freq() failed: (%d)\n", result); 62 | } 63 | result = hackrf_set_amp_enable(device, 1); 64 | if (result != HACKRF_SUCCESS) { 65 | fprintf(stderr, "hackrf_set_amp_enable() failed: (%d)\n", result); 66 | } 67 | result = hackrf_set_txvga_gain(device, last_gain_tx); 68 | if (result != HACKRF_SUCCESS) { 69 | fprintf(stderr, "hackrf_set_txvga_gain() failed: (%d)\n", result); 70 | } 71 | result = hackrf_start_tx(device, tx_callback, this); 72 | if (result != HACKRF_SUCCESS) { 73 | fprintf(stderr, "hackrf_start_tx() failed: (%d)\n", result); 74 | } 75 | tx_started = true; 76 | } 77 | 78 | void RfCar::stopTx() 79 | { 80 | if (always_tx) { 81 | return; 82 | } 83 | int result = hackrf_stop_tx(device); 84 | if (result != HACKRF_SUCCESS) { 85 | fprintf(stderr, "hackrf_stop_tx() failed: (%d)\n", result); 86 | } 87 | tx_started = false; 88 | result = hackrf_close(device); 89 | if (result != HACKRF_SUCCESS) { 90 | fprintf(stderr, "hackrf_close() failed: (%d)\n", result); 91 | } 92 | } 93 | 94 | void RfCar::changeState(Direction dir, int gain_tx) 95 | { 96 | if (gain_tx != last_gain_tx) { 97 | last_gain_tx = gain_tx; 98 | if (tx_started) { 99 | int result = hackrf_set_txvga_gain(device, last_gain_tx); 100 | if (result != HACKRF_SUCCESS) { 101 | fprintf(stderr, "hackrf_set_txvga_gain() failed: (%d)\n", result); 102 | } 103 | } 104 | } 105 | if (!supportDirection(dir)) { 106 | return; 107 | } 108 | if (dir != last_dir) { 109 | if (last_dir == NONE) { 110 | last_dir = dir; 111 | pos = 0; 112 | startTx(); 113 | return; 114 | } else if (dir == NONE) { 115 | txEnd(); 116 | stopTx(); 117 | } 118 | last_dir = dir; 119 | pos = 0; 120 | } 121 | } 122 | 123 | void FskCar::txEnd() 124 | { 125 | last_dir = STOP; 126 | pos = 0; 127 | int spb = sample_rate / symbol_rate; // samples per bit 128 | while ((int)pos/spb < pattern_size) { 129 | // "the right way" is to use condition variable but this is more simple and works fine 130 | usleep(1000); 131 | } 132 | } 133 | 134 | int FskCar::txCallback(uint8_t* buffer, int valid_length) 135 | { 136 | int spb = sample_rate / symbol_rate; // samples per bit 137 | for (int i = 0 ; i < valid_length/2 ; i++) { 138 | int ind = pos/spb % pattern_size; 139 | std::unordered_map &pattern = patterns[last_dir]; 140 | auto it = pattern.find(ind); 141 | if (it != pattern.end()) { 142 | int bit = it->second; 143 | float freq = bit == 0 ? 1000000 : 1500000; 144 | float phase_change_per_sample = (2*M_PI * freq) / sample_rate; 145 | buffer[i*2] = cos(phase) * SCHAR_MAX; 146 | buffer[i*2+1] = sin(phase) * SCHAR_MAX; 147 | phase += phase_change_per_sample; 148 | if (phase > 2*M_PI) { 149 | phase -= 2*M_PI; 150 | } 151 | } else { 152 | buffer[i*2] = 0; 153 | buffer[i*2+1] = 0; 154 | phase = 0; 155 | } 156 | pos++; 157 | } 158 | return 0; 159 | } 160 | 161 | static void push_map(std::unordered_map &fsk_map, int ind, const std::string &data) 162 | { 163 | for (auto b : data) { 164 | fsk_map[ind++] = b == '0' ? 0 : 1; 165 | } 166 | } 167 | 168 | Dickie::Dickie(uint64_t freq, int sample_rate, int symbol_rate, bool always_tx) : FskCar(freq, sample_rate, symbol_rate, always_tx) { 169 | std::string fwd_right_bits[4] = { 170 | "10101010101010101100101100001010010001000110110100001111000011110000111000110110100010001111110111110110110000001011100100101010000101010110100000", 171 | "10101010101010101100101100001010010001000110110100001111000011110000111000110000100010001111110111110110110000001011100100101010101101000100110111", 172 | "10101010101010101100101100001010010001000110110100001111000011110000111000110010100010001111110111110110110000001011100100101010110101001010111000", 173 | "10101010101010101100101100001010010001000110110100001111000011110000111000110100100010001111110111110110110000001011100100101010011101011000101111" 174 | }; 175 | std::string back_right_bits[4] = { 176 | "10101010101010101100101100001010010001000110110100001111000011110000111000110000100001001111000111110110110000001011100100101010001101001000010111", 177 | "10101010101010101100101100001010010001000110110100001111000011110000111000110010100001001111000111110110110000001011100100101010010101000110011000", 178 | "10101010101010101100101100001010010001000110110100001111000011110000111000110100100001001111000111110110110000001011100100101010111101010100001111", 179 | "10101010101010101100101100001010010001000110110100001111000011110000111000110110100001001111000111110110110000001011100100101010100101011010000000" 180 | }; 181 | std::string fwd_bits[4] = { 182 | "10101010101010101100101100001010010001000110110100001111000011110000111000110100100010101111111111110110110000001011100100101010101110100100100000", 183 | "10101010101010101100101100001010010001000110110100001111000011110000111000110110100010101111111111110110110000001011100100101010110110101010101111", 184 | "10101010101010101100101100001010010001000110110100001111000011110000111000110000100010101111111111110110110000001011100100101010011110111000111000", 185 | "10101010101010101100101100001010010001000110110100001111000011110000111000110010100010101111111111110110110000001011100100101010000110110110110111" 186 | }; 187 | std::string back_bits[4] = { 188 | "10101010101010101100101100001010010001000110110100001111000011110000111000110110100001011111000011110110110000001011100100101010011110100101000111", 189 | "10101010101010101100101100001010010001000110110100001111000011110000111000110000100001011111000011110110110000001011100100101010110110110111010000", 190 | "10101010101010101100101100001010010001000110110100001111000011110000111000110010100001011111000011110110110000001011100100101010101110111001011111", 191 | "10101010101010101100101100001010010001000110110100001111000011110000111000110100100001011111000011110110110000001011100100101010000110101011001000" 192 | }; 193 | std::string fwd_left_bits[4] = { 194 | "10101010101010101100101100001010010001000110110100001111000011110000111000110110100000101111011111110110110000001011100100101010110101011100010000", 195 | "10101010101010101100101100001010010001000110110100001111000011110000111000110000100000101111011111110110110000001011100100101010011101001110000111", 196 | "10101010101010101100101100001010010001000110110100001111000011110000111000110010100000101111011111110110110000001011100100101010000101000000001000", 197 | "10101010101010101100101100001010010001000110110100001111000011110000111000110100100000101111011111110110110000001011100100101010101101010010011111" 198 | }; 199 | std::string back_left_bits[4] = { 200 | "10101010101010101100101100001010010001000110110100001111000011110000111000110110100000011111010011110110110000001011100100101010111101011111011000", 201 | "10101010101010101100101100001010010001000110110100001111000011110000111000110000100000011111010011110110110000001011100100101010010101001101001111", 202 | "10101010101010101100101100001010010001000110110100001111000011110000111000110010100000011111010011110110110000001011100100101010001101000011000000", 203 | "10101010101010101100101100001010010001000110110100001111000011110000111000110100100000011111010011110110110000001011100100101010100101010001010111" 204 | }; 205 | std::string stop_bits[4] = { 206 | "10101010101010101100101100001010010001000110110100001111000011110000111000110010100000001111010111110110110000001011100100101010110110111100000111", 207 | "10101010101010101100101100001010010001000110110100001111000011110000111000110100100000001111010111110110110000001011100100101010011110101110010000", 208 | "10101010101010101100101100001010010001000110110100001111000011110000111000110110100000001111010111110110110000001011100100101010000110100000011111", 209 | "10101010101010101100101100001010010001000110110100001111000011110000111000110010100000001111010111110110110000001011100100101010110110111100000111" 210 | }; 211 | int spb = sample_rate / symbol_rate; // samples per bit 212 | int long_pause = (0.003641 * sample_rate) / spb; // long pause 213 | int short_pause = (0.000355 * sample_rate) / spb; // short pause 214 | int map_ind = 0; 215 | 216 | map_ind += long_pause; 217 | for (int i = 0 ; i < 9 ; i++) { 218 | for (int j = 0 ; j < 16 ; j++) { 219 | push_map(patterns[FWD_RIGHT], map_ind, fwd_right_bits[i%4]); 220 | push_map(patterns[BACK_RIGHT], map_ind, back_right_bits[i%4]); 221 | push_map(patterns[FWD], map_ind, fwd_bits[i%4]); 222 | push_map(patterns[BACK], map_ind, back_bits[i%4]); 223 | push_map(patterns[FWD_LEFT], map_ind, fwd_left_bits[i%4]); 224 | push_map(patterns[BACK_LEFT], map_ind, back_left_bits[i%4]); 225 | push_map(patterns[STOP], map_ind, stop_bits[i%4]); 226 | map_ind += 146; 227 | map_ind += short_pause; 228 | } 229 | map_ind += long_pause; 230 | } 231 | pattern_size = map_ind; 232 | 233 | std::string sync1_bits[4] = { 234 | "10101010101010101011010010110100101101001100101011001010110010101100101000110000000001111100001100011001011101010010000000101010101001011111100000", 235 | "10101010101010101011010010110100101101001100101011001010110010101100101000110010000001111100001100011001011101010010000000101010110001010001101111", 236 | "10101010101010101011010010110100101101001100101011001010110010101100101000110100000001111100001100011001011101010010000000101010011001000011111000", 237 | "10101010101010101011010010110100101101001100101011001010110010101100101000110110000001111100001100011001011101010010000000101010000001001101110111" 238 | }; 239 | std::string sync2_bits = 240 | "10101010101010101100101100001010010001000110110100001111000011110000111000110010100000001111010100111011000011011011100100101010001101010100010111"; 241 | map_ind = 0; 242 | map_ind += long_pause; 243 | for (int i = 0 ; i < 4 ; i++) { 244 | for (int j = 0 ; j < 16 ; j++) { 245 | push_map(patterns[SYNC], map_ind, sync1_bits[i]); 246 | map_ind += 146; 247 | map_ind += short_pause; 248 | } 249 | map_ind += long_pause; 250 | } 251 | map_ind += (0.01 * sample_rate) / spb; 252 | for (int k = 0 ; k < 5 ; k++) { 253 | for (int i = 0 ; i < 4 ; i++) { 254 | for (int j = 0 ; j < 16 ; j++) { 255 | push_map(patterns[SYNC], map_ind, sync2_bits); 256 | map_ind += 146; 257 | map_ind += short_pause; 258 | } 259 | map_ind += long_pause; 260 | } 261 | } 262 | sync_pattern_size = map_ind; 263 | } 264 | 265 | void Dickie::sendSync() 266 | { 267 | last_dir = SYNC; 268 | pos = 0; 269 | startTx(); 270 | int spb = sample_rate / symbol_rate; // samples per bit 271 | while ((int)pos/spb < sync_pattern_size) { 272 | // "the right way" is to use condition variable but this is more simple and works fine 273 | usleep(1000); 274 | } 275 | stopTx(); 276 | last_dir = NONE; 277 | } 278 | 279 | Concept::Concept(uint64_t freq, int sample_rate, int symbol_rate, bool always_tx) : FskCar(freq, sample_rate, symbol_rate, always_tx) { 280 | std::string fwd_right_bits[4] = { 281 | "11010101010101010101111101000111011110010000000110111101101111011011110110001000001111000011111101001010101011011000110010100101010111111100010110000000111000001100", 282 | "11010101010101010101111101000111011110010000000110111101101111011011110110001000011111000011111101001010101011011000110010100101010111111100010110011000001011000011", 283 | "11010101010101010101111101000111011110010000000110111101101111011011110110001000101111000011111101001010101011011000110010100101010111111100010110010011011111010011", 284 | "11010101010101010101111101000111011110010000000110111101101111011011110110001000111111000011111101001010101011011000110010100101010111111100010110001011101100011100" 285 | }; 286 | std::string back_right_bits[4] = { 287 | "11010101010101010101111101000111011110010000000110111101101111011011110110001000001111000011111101001010101011011000110100100101010111111100011000010011011100010011", 288 | "11010101010101010101111101000111011110010000000110111101101111011011110110001000011111000011111101001010101011011000110100100101010111111100011000001011101111011100", 289 | "11010101010101010101111101000111011110010000000110111101101111011011110110001000101111000011111101001010101011011000110100100101010111111100011000000000111011001100", 290 | "11010101010101010101111101000111011110010000000110111101101111011011110110001000111111000011111101001010101011011000110100100101010111111100011000011000001000000011" 291 | }; 292 | std::string fwd_bits[4] = { 293 | "11010101010101010101111101000111011110010000000110111101101111011011110110001000001111000011111101001010101011011000100011100101010111111100000111011011001000000000", 294 | "11010101010101010101111101000111011110010000000110111101101111011011110110001000011111000011111101001010101011011000100011100101010111111100000111000011111011001111", 295 | "11010101010101010101111101000111011110010000000110111101101111011011110110001000101111000011111101001010101011011000100011100101010111111100000111001000101111011111", 296 | "11010101010101010101111101000111011110010000000110111101101111011011110110001000111111000011111101001010101011011000100011100101010111111100000111010000011100010000" 297 | }; 298 | std::string back_bits[4] = { 299 | "11010101010101010101111101000111011110010000000110111101101111011011110110001000001111000011111101001010101011011000111100000101010111111100010000111110100100010100", 300 | "11010101010101010101111101000111011110010000000110111101101111011011110110001000011111000011111101001010101011011000111100000101010111111100010000100110010111011011", 301 | "11010101010101010101111101000111011110010000000110111101101111011011110110001000101111000011111101001010101011011000111100000101010111111100010000101101000011001011", 302 | "11010101010101010101111101000111011110010000000110111101101111011011110110001000111111000011111101001010101011011000111100000101010111111100010000110101110000000100" 303 | }; 304 | std::string fwd_left_bits[4] = { 305 | "11010101010101010101111101000111011110010000000110111101101111011011110110001000001111000011111101001010101011011000110111100101010111111100011011011011100110101100", 306 | "11010101010101010101111101000111011110010000000110111101101111011011110110001000011111000011111101001010101011011000110111100101010111111100011011000011010101100011", 307 | "11010101010101010101111101000111011110010000000110111101101111011011110110001000101111000011111101001010101011011000110111100101010111111100011011001000000001110011", 308 | "11010101010101010101111101000111011110010000000110111101101111011011110110001000111111000011111101001010101011011000110111100101010111111100011011010000110010111100" 309 | }; 310 | std::string back_left_bits[4] = { 311 | "11010101010101010101111101000111011110010000000110111101101111011011110110001000001111000011111101001010101011011000110110000101010111111100011010101110111011010011", 312 | "11010101010101010101111101000111011110010000000110111101101111011011110110001000011111000011111101001010101011011000110110000101010111111100011010110110001000011100", 313 | "11010101010101010101111101000111011110010000000110111101101111011011110110001000101111000011111101001010101011011000110110000101010111111100011010111101011100001100", 314 | "11010101010101010101111101000111011110010000000110111101101111011011110110001000111111000011111101001010101011011000110110000101010111111100011010100101101111000011" 315 | }; 316 | std::string stop_bits[4] = { 317 | "11010101010101010101111101000111011110010000000110111101101111011011110110001000001111000011111101001010101011011000110110100101010111111100011010000010001111111000", 318 | "11010101010101010101111101000111011110010000000110111101101111011011110110001000011111000011111101001010101011011000110110100101010111111100011010011010111100110111", 319 | "11010101010101010101111101000111011110010000000110111101101111011011110110001000101111000011111101001010101011011000110110100101010111111100011010010001101000100111", 320 | "11010101010101010101111101000111011110010000000110111101101111011011110110001000111111000011111101001010101011011000110110100101010111111100011010001001011011101000" 321 | }; 322 | int spb = sample_rate / symbol_rate; // samples per bit 323 | int long_pause = (0.006285 * sample_rate) / spb; // long pause 324 | int short_pause = (0.000393 * sample_rate) / spb; // short pause 325 | int map_ind = 0; 326 | 327 | map_ind += long_pause; 328 | for (int i = 0 ; i < 4 ; i++) { 329 | push_map(patterns[FWD_RIGHT], map_ind, fwd_right_bits[i%4]); 330 | push_map(patterns[BACK_RIGHT], map_ind, back_right_bits[i%4]); 331 | push_map(patterns[FWD], map_ind, fwd_bits[i%4]); 332 | push_map(patterns[BACK], map_ind, back_bits[i%4]); 333 | push_map(patterns[FWD_LEFT], map_ind, fwd_left_bits[i%4]); 334 | push_map(patterns[BACK_LEFT], map_ind, back_left_bits[i%4]); 335 | push_map(patterns[STOP], map_ind, stop_bits[i%4]); 336 | map_ind += 164; 337 | map_ind += short_pause; 338 | } 339 | map_ind += long_pause - short_pause; 340 | pattern_size = map_ind; 341 | 342 | std::string sync1_bits[4] = { 343 | "11010101010101010101111101000111011110010000000110111101101111011011110110001000001111000011111101001010101011011000110110100101010111111100011010000010001111111000", 344 | "11010101010101010101111101000111011110010000000110111101101111011011110110001000011111000011111101001010101011011000110110100101010111111100011010011010111100110111", 345 | "11010101010101010101111101000111011110010000000110111101101111011011110110001000101111000011111101001010101011011000110110100101010111111100011010010001101000100111", 346 | "11010101010101010101111101000111011110010000000110111101101111011011110110001000111111000011111101001010101011011000110110100101010111111100011010001001011011101000" 347 | }; 348 | 349 | map_ind = 0; 350 | map_ind += long_pause; 351 | for (int i = 0 ; i < 4 ; i++) { 352 | push_map(patterns[SYNC], map_ind, sync1_bits[i]); 353 | map_ind += 164; 354 | map_ind += short_pause; 355 | } 356 | map_ind += long_pause - short_pause; 357 | sync_pattern_size = map_ind; 358 | } 359 | 360 | void Concept::sendSync() 361 | { 362 | last_dir = SYNC; 363 | pos = 0; 364 | startTx(); 365 | int spb = sample_rate / symbol_rate; // samples per bit 366 | while ((int)pos/spb < sync_pattern_size) { 367 | // "the right way" is to use condition variable but this is more simple and works fine 368 | usleep(1000); 369 | } 370 | stopTx(); 371 | last_dir = NONE; 372 | } 373 | 374 | RangeRover::RangeRover(uint64_t freq, int sample_rate, int symbol_rate, bool always_tx) : FskCar(freq, sample_rate, symbol_rate, always_tx) { 375 | std::string fwd_bits = "01010101010101010101010101010100110000110101001010100110010110111000011110000110011001100111100110011000011110011001100110000110011001100111100110000111100001111001100110011000011110000110011110011000011110000110011110000111100110011000011001111000011001100111100001111000011110011000011001111001100001100111100110011000011110000111100110011000011110011000011001100110011001111001100001111001100001100110011001111001100001111000011110011001100001111001100110000111100110000111100110000110011110000111100110000111100001111001100110011000011110011001100001100111"; 376 | std::string back_bits = "01010101010101010101010101010100110000110101001010100110010110111000011110000110011001100111100110011000011110011001100110000110011001100111100110000111100001111001100110011000011110000110011110011000011110000110011110000111100110011000011001111000011001100111100001111000011110011000011001111001100001100111100110011000011110000111100110011000011110011000011001100110011001111001100001111001100001100110011001111001100001111000011110011000011110011001100110000111100110011001100110000110011110000111100001100111100001111000011001100111100001111000011110011001"; 377 | std::string left_bits = "01010101010101010101010101010100110000110101001010100110010110111000011110000110011001100111100110011000011110011001100110000110011001100111100110000111100001111001100110011000011110000110011110011000011110000110011110000111100110011000011001111000011001100111100001111000011110011000011001111001100001100111100110011000011110000111100110011000011110011000011001100110011001111001100001111001100001100110011001111001100001111000011110000111100110011001100110000111100001111001100110000110011110000110011001100111100001100111100110011000011001100111100001111001"; 378 | std::string right_bits = "01010101010101010101010101010100110000110101001010100110010110111000011110000110011001100111100110011000011110011001100110000110011001100111100110000111100001111001100110011000011110000110011110011000011110000110011110000111100110011000011001111000011001100111100001111000011110011000011001111001100001100111100110011000011110000111100110011000011110011000011001100110011001111001100001111001100001100110011001111001100001111000011001111001100110011001100110000110011001111001100110000110011110011000011001100111100110011000011001100110011110011000011001111001"; 379 | std::string fwdleft_bits = "01010101010101010101010101010100110000110101001010100110010110111000011110000110011001100111100110011000011110011001100110000110011001100111100110000111100001111001100110011000011110000110011110011000011110000110011110000111100110011000011001111000011001100111100001111000011110011000011001111001100001100111100110011000011110000111100110011000011110011000011001100110011001111001100001111001100001100110011001111001100001111000011110000111100001111001100110000111100001111000011110000110011110000110011001111001100001100110011110000110011001100111100110011001"; 380 | std::string fwdright_bits = "01010101010101010101010101010100110000110101001010100110010110111000011110000110011001100111100110011000011110011001100110000110011001100111100110000111100001111001100110011000011110000110011110011000011110000110011110000111100110011000011001111000011001100111100001111000011110011000011001111001100001100111100110011000011110000111100110011000011110011000011001100110011001111001100001111001100001100110011001111001100001111000011001111001100001111001100110000110011001111000011110000110011110011000011001111001100110011001100001111000011110011000011110011001"; 381 | std::string backleft_bits = "01010101010101010101010101010100110000110101001010100110010110111000011110000110011001100111100110011000011110011001100110000110011001100111100110000111100001111001100110011000011110000110011110011000011110000110011110000111100110011000011001111000011001100111100001111000011110011000011001111001100001100111100110011000011110000111100110011000011110011000011001100110011001111001100001111001100001100110011001111001100001111000011110000110011110011001100110000111100001100111100110000110011110000110011110000111100001111001100001111000011001100110011001111001"; 382 | std::string backright_bits = "01010101010101010101010101010100110000110101001010100110010110111000011110000110011001100111100110011000011110011001100110000110011001100111100110000111100001111001100110011000011110000110011110011000011110000110011110000111100110011000011001111000011001100111100001111000011110011000011001111001100001100111100110011000011110000111100110011000011110011000011001100110011001111001100001111001100001100110011001111001100001111000011001111000011110011001100110000110011001100111100110000110011110011000011110000111100110000110011110000110011110011001100001111001"; 383 | std::string stop_bits = "01010101010101010101010101010100110000110101001010100110010110111000011110000110011001100111100110011000011110011001100110000110011001100111100110000111100001111001100110011000011110000110011110011000011110000110011110000111100110011000011001111000011001100111100001111000011110011000011001111001100001100111100110011000011110000111100110011000011110011000011001100110011001111001100001111001100001100110011001111001100001111000011110011001100110011001100110000111100110000110011001111001100110011000011001111001100001111001100001111000011110000110011001100111"; 384 | 385 | int spb = sample_rate / symbol_rate; // samples per bit 386 | int short_pause = (0.0012 * sample_rate) / spb; 387 | int long_pause = (0.0221 * sample_rate) / spb; 388 | int map_ind = 0; 389 | 390 | for (int i = 0; i < 4; i++) { 391 | for (int j = 0; j < 2; j++) { 392 | push_map(patterns[FWD_RIGHT], map_ind, fwdright_bits); 393 | push_map(patterns[BACK_RIGHT], map_ind, backright_bits); 394 | push_map(patterns[FWD], map_ind, fwd_bits); 395 | push_map(patterns[BACK], map_ind, back_bits); 396 | push_map(patterns[FWD_LEFT], map_ind, fwdleft_bits); 397 | push_map(patterns[BACK_LEFT], map_ind, backleft_bits); 398 | push_map(patterns[LEFT], map_ind, left_bits); 399 | push_map(patterns[RIGHT], map_ind, right_bits); 400 | push_map(patterns[STOP], map_ind, stop_bits); 401 | map_ind += 560; 402 | map_ind += short_pause; 403 | } 404 | map_ind += long_pause; 405 | } 406 | 407 | pattern_size = map_ind; 408 | } 409 | 410 | static void make_short_pulses(std::vector &v, int num) 411 | { 412 | for (int i = 0 ; i < num ; i++) { 413 | v.push_back(1); 414 | v.push_back(0); 415 | } 416 | } 417 | 418 | OokCar::OokCar(uint64_t freq, int sample_rate, int symbol_rate, bool always_tx) : RfCar(freq, sample_rate, symbol_rate, always_tx) 419 | { 420 | for (int i = 0 ; i < 8 ; i++) { 421 | // each pattern start with 4 long pulses 422 | for (int j = 0 ; j < 4 ; j++) { 423 | patterns[i].push_back(1); 424 | patterns[i].push_back(1); 425 | patterns[i].push_back(1); 426 | patterns[i].push_back(0); 427 | } 428 | } 429 | make_short_pulses(patterns[FWD], 10); 430 | make_short_pulses(patterns[FWD_LEFT], 28); 431 | make_short_pulses(patterns[FWD_RIGHT], 34); 432 | make_short_pulses(patterns[BACK], 40); 433 | make_short_pulses(patterns[BACK_LEFT], 52); 434 | make_short_pulses(patterns[BACK_RIGHT], 46); 435 | make_short_pulses(patterns[LEFT], 58); 436 | make_short_pulses(patterns[RIGHT], 64); 437 | patterns[NONE].push_back(0); 438 | patterns[NONE].push_back(0); 439 | patterns[NONE].push_back(0); 440 | // moving averarge can be implemented more efficiently 441 | // but this allows playing with other type of filters 442 | for (int i = 0 ; i < 20 ; i++) { 443 | filter.push_back(0.9/20); 444 | } 445 | } 446 | 447 | void OokCar::invertSteering() { 448 | patterns[LEFT].swap(patterns[RIGHT]); 449 | patterns[FWD_LEFT].swap(patterns[FWD_RIGHT]); 450 | patterns[BACK_LEFT].swap(patterns[BACK_RIGHT]); 451 | } 452 | 453 | void OokCar::invertThrottle() { 454 | patterns[FWD].swap(patterns[BACK]); 455 | patterns[FWD_LEFT].swap(patterns[BACK_LEFT]); 456 | patterns[FWD_RIGHT].swap(patterns[BACK_RIGHT]); 457 | } 458 | 459 | int OokCar::txCallback(uint8_t *buffer, int valid_length) 460 | { 461 | int spb = sample_rate / symbol_rate; // samples per bit 462 | for (int i = 0; i < valid_length / 2; i++) 463 | { 464 | std::vector &pattern = patterns[last_dir]; 465 | int pattern_size = pattern.size(); 466 | float sum = 0; 467 | for (int j = 0; j < (int)filter.size(); j++) 468 | { 469 | int sample = pattern[((pos + j) / spb) % pattern_size]; 470 | sum += filter[j] * sample; 471 | } 472 | pos += 1; 473 | buffer[i * 2] = sum * 127; 474 | buffer[i * 2 + 1] = 0; 475 | } 476 | return 0; 477 | } 478 | --------------------------------------------------------------------------------