├── README.md ├── bin ├── README.txt ├── gps-sdr-sim └── gps-sdr-sim_x64 ├── date2.txt ├── images ├── LCD.png ├── WALB.gif ├── WALB.png ├── WALB2.png ├── ads-b_iq.png ├── adsb_wave.png ├── iq_signal.png ├── rotary_enc.png ├── schematic.png └── test_setup2.png ├── level2.txt ├── menu2.txt ├── python ├── replay.ini └── replay.py ├── replay2.c ├── scritps ├── .gitkeep ├── ads-b.sh ├── eth.sh ├── i2c-disp.sh ├── kill_proc.sh ├── sim_start.sh ├── stat.sh └── wlan.sh └── webui ├── LatLon.css ├── LatLon.ini.php ├── LatLon.js ├── LatLon.php ├── LatLon.tmpl.php ├── README.md ├── kill_proc.sh ├── smooth2.php └── start2.php /README.md: -------------------------------------------------------------------------------- 1 | # WALB ( Wireless Attack Launch Box ) 2 | ## What is WALB ? 3 | * WALB is a Raspberry Pi2/Pi3 and HackRF based lunch box sized portable RF signal generator. 4 | * The intended purpose of the WALB development is to test or demonstrate the security issue of wireless devices and location based applications. 5 | * By preparing a I/Q binary data, it is possible to generate any signal in the frequency range available to HackRF. 6 | * For GPS and ADS-B, real time signal generator module is included in WALB. 7 | * It uses HackRF as a SDR unit with enhanced GPS-SDR-SIM for GPS signal generation. 8 | * It has a 8x2 LCD and a rotary encoder with two color LED and a push switch for the operation of WALB. 9 | * Since WALB works with battery powered, you can use it any where you like. 10 | * Adding new simulation scenario or signal generation, it can be achieved by SSH login and simply edit the menu items using your favorite text editor. 11 | * By preparing the binary I / Q signal file of 8 bit signed, you can generate arbitrary RF signals. 12 | * To do so, you simply need to edit and add TEXT menu items specifying the filename of I/Q file, frequency, and sample rate. 13 | * If you prepare an external program to generate the I / Q signal in real time,you can also add the program and/or script in the menu. 14 | * You can set or chose GPS spoofing scenario by predefined location and/or date & time. 15 | 16 | ![PICT](https://github.com/crecentmoon/WALB/blob/master/images/WALB.png) 17 | 18 | ## Prerequisites. 19 | You need to install GPS-SDR-SIM,HackRF host tools, and WireringPi on RaspberryPi.
20 | Rapsberry Pi3 is highly recomended for better performance of the real time signal generation.
21 | It requires apache2 and php5 if you wish to use web based UI.
22 | 23 | The installation instruction links are as follows.
24 | RaspberryPi: 25 | https://www.raspberrypi.org/
26 | GPS-SDR-SIM: 27 | https://github.com/osqzss/gps-sdr-sim
28 | HackRF: 29 | https://github.com/mossmann/hackrf
30 | WireringPi: 31 | http://wiringpi.com/download-and-install/ 32 |

33 | Additional info to control hackrf via HTTP server.
34 | * You have to set udev rules.
35 | ``` 36 | (1)Add /etc/udev/rules.d/52-hackrf.rules as follows. 37 | ATTR{idVendor}=="1d50", ATTR{idProduct}=="604b", SYMLINK+="hackrf-jawbreaker-%k", MODE="660", GROUP="plugdev" 38 | ATTR{idVendor}=="1d50", ATTR{idProduct}=="6089", SYMLINK+="hackrf-one-%k", MODE="660", GROUP="plugdev" 39 | ATTR{idVendor}=="1fc9", ATTR{idProduct}=="000c", SYMLINK+="hackrf-dfu-%k", MODE="660", GROUP="plugdev" 40 | (2) Refrect udev rules. 41 | $sudo udevadm control --reload-rules 42 | (3) Add www-data user into plugdev. 43 | $sudo usermod -aG plugdev www-data 44 | (4) Restart Apache2 45 | $sudo systemctl restart apache2 46 | ``` 47 | 48 | ## directory structure of WALB software:
49 | ``` 50 | /home/pi/ 51 | /IQ-files ... binary I/Q files to pass hackRF or text files used for genaration of I/Q file by 52 | dedicated real time signal generation program such as enhanced GPS-SDR-SIM, or ADS-B_gen 53 | /bin/gps-sdr-sim ... enhanced GPS-SDR-SIM binary file and ephemeris file(s). 54 | replay2 ... Main startup program of the WALB 55 | menu2.txt ... Main menu items displayed on LCD 56 | level2.txt ... Sub menu-1: transmit power setteing 57 | date2.txt ... Sub menu-2: date&time setting for GPS time spoofing 58 | scripts/ 59 | sim_start.sh ... Script to start I/Q signal generation and kick HackRF to transmit 60 | ic2-disp.sh ... Script to control LCD 61 | stat.sh ... Script to check if hackrf_transfer is active 62 | kill_proc.sh ... Script to kill gps-sdr-sim and/or hackrf_transfer 63 | eth.sh ... Script to display eth0 IP address on LCD 64 | wlan.sh ... Script to display wlan0 IP address on LCD 65 | /python/    ... Python port file from replay2.c 66 | 67 | /var/www/html/webui/ ... Sctipt files to set GPS-SDR-SIM location via Web UI. 68 | LatLon.php ... Web UI 69 | LatLon.ini.php ... 70 | LatLon.tmpl.php ... Template file. 71 | LatLon.js ... 72 | start2.php ... Start/Stop GPS signal generation 73 | kill_proc.php ... Kill process of hackrf_transfer, gps-sdr-sim and smooth.php for terminate signal generation. 74 | smooth2.php ... Script for location interpolation between previos position and latest clicked position. 75 | ``` 76 | ## Installation 77 | # compile 78 | gcc replay2.c -I/usr/local/include -L/usr/local/lib -lwiringPi -o replay2
79 | (You may need to adjust -I/Lxxxxx for the location of wireringPi)
80 | 81 | ## Usage demo links.
82 | * Wireless Attack Launch Box operation demo. (Length: 3 minutes)
83 | https://www.youtube.com/watch?v=SIPCqLmJFig
84 |
85 | * Real time GPS signal generation by WALB. (Length: 2 minutes)
86 | https://www.youtube.com/watch?v=-V4KLIqEzQg
87 |
88 | * GPS time spoofing demo intended to test GPS week number rollover issue. (Length: 140 seconds)
89 | https://www.youtube.com/watch?v=mEU5RjRJ2lI
90 |
91 | * ADS-B replay attack demo.( Length: 2 minites)
92 | https://www.youtube.com/watch?v=APc1hreOkYU
93 | -------------------------------------------------------------------------------- /bin/README.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------- 2 | gps-sdr-sim_x64 for Ubuntu16.04 based on https://github.com/osqzss/gps-sdr-sim 3 | -------------------------------------------------------------- 4 | This binary file is compiled for x64 CPU on Ubuntu16.04. 5 | Added feature is interactive location (Lat,Lon,Alt) update mode. 6 | You can specify a file which contains one line Lat,Lon,Alt. 7 | Use external program to update the contents of the file. 8 | 9 | Usage: gps-sdr-sim [options] 10 | Options: 11 | -e RINEX navigation file for GPS ephemerides (required) 12 | -u User motion file (dynamic mode) 13 | -g NMEA GGA stream (dynamic mode) 14 | -c ECEF X,Y,Z in meters (static mode) e.g. 3967283.154,1022538.181,4872414.484 15 | * -l Lat,Lon,Hgt (static mode) e.g. 35.681298,139.766247,10.0 | FILE_NAME 16 | -t Scenario start time YYYY/MM/DD,hh:mm:ss 17 | -T Overwrite TOC and TOE to scenario start time 18 | -d Duration [sec] (dynamic mode max: 300, static mode max: 86400) 19 | -o I/Q sampling data file (default: gpssim.bin) 20 | -s Sampling frequency [Hz] (default: 2600000) 21 | -b I/Q data format [1/8/16] (default: 16) 22 | -i Disable ionospheric delay for spacecraft scenario 23 | -v Show details about simulated channels 24 | 25 | --------------------------------------------------------- 26 | gps-sdr-sim 27 | --------------------------------------------------------- 28 | This binary file is enhanced version of GPS-SDR-SIM, which is compiled for raspberry Pi 2/3 and HackRF as a SDR unit. 29 | 30 | Options: * denotes enhanced feature. 31 | -e RINEX navigation file for GPS ephemerides (required) 32 | -u User motion file (dynamic mode) 33 | -g NMEA GGA stream (dynamic mode) 34 | -l Lat,Lon,Hgt (static mode) e.g. 30.286502,120.032669,100 35 | * -i Interactive input from a file Lat,Lon,Alt 36 | -t Scenario start time YYYY/MM/DD,hh:mm:ss 37 | * -T Scenario start time YYYY/MM/DD,hh:mm:ss(No ristriction) 38 | -d Duration [sec] (max: 3000) 39 | -o I/Q sampling data file (default: gpssim.bin) 40 | -s Sampling frequency [Hz] (default: 2600000) 41 | -b I/Q data format [1/8/16] (default: 16) 42 | * -n Number of channels [1..16] (default: 16) 43 | -v Show details about simulated channels 44 | 45 | Usage example for interactive mode: 46 | ----------------------------------- 47 | STEP 1) run script to start enhanced gps-sdr-sim 48 | #!/bin/sh 49 | SAMPLE=2048000 50 | POWER=0 51 | BRDC=brdc3640.15n 52 | N_SAT=16 53 | FIFO=/tmp/fifo 54 | INT_FILE=/tmp/latlon.txt 55 | DATE=2016/08/23,00:18:22 56 | if [ ! -e $FIFO ]; then 57 | mkfifo $FIFO 58 | fi 59 | ./gps-sdr-sim -s $SAMPLE -e $BRDC -i $INT_FILE -b8 -n $N_SAT -o $FIFO -T $DATE& 60 | hackrf_transfer -t $FIFO -f $FREQ -s $SAMPLE -x $POWER >/dev/nul 61 | 62 | STEP 2) Update the content of file INT_FILE every 1/10 second as necessary. 63 | 64 | Notes: 65 | I have reduced the sample rate a bit from default(2600000) due to performance limitation of the raspberry Pi. 66 | You need additional user interface scripts to update INT_FILE as necessary. ( such as under "webui" directory ) 67 | Use Google maps API for example. 68 | -------------------------------------------------------------------------------- /bin/gps-sdr-sim: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crescentvenus/WALB/c28f355020981619fb61770a7757ff2eff3471a0/bin/gps-sdr-sim -------------------------------------------------------------------------------- /bin/gps-sdr-sim_x64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crescentvenus/WALB/c28f355020981619fb61770a7757ff2eff3471a0/bin/gps-sdr-sim_x64 -------------------------------------------------------------------------------- /date2.txt: -------------------------------------------------------------------------------- 1 | Now 2 | ORG 3 | 3Y+ 4 | 3Y- 5 | 1D+ 6 | 1D- -------------------------------------------------------------------------------- /images/LCD.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crescentvenus/WALB/c28f355020981619fb61770a7757ff2eff3471a0/images/LCD.png -------------------------------------------------------------------------------- /images/WALB.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crescentvenus/WALB/c28f355020981619fb61770a7757ff2eff3471a0/images/WALB.gif -------------------------------------------------------------------------------- /images/WALB.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crescentvenus/WALB/c28f355020981619fb61770a7757ff2eff3471a0/images/WALB.png -------------------------------------------------------------------------------- /images/WALB2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crescentvenus/WALB/c28f355020981619fb61770a7757ff2eff3471a0/images/WALB2.png -------------------------------------------------------------------------------- /images/ads-b_iq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crescentvenus/WALB/c28f355020981619fb61770a7757ff2eff3471a0/images/ads-b_iq.png -------------------------------------------------------------------------------- /images/adsb_wave.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crescentvenus/WALB/c28f355020981619fb61770a7757ff2eff3471a0/images/adsb_wave.png -------------------------------------------------------------------------------- /images/iq_signal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crescentvenus/WALB/c28f355020981619fb61770a7757ff2eff3471a0/images/iq_signal.png -------------------------------------------------------------------------------- /images/rotary_enc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crescentvenus/WALB/c28f355020981619fb61770a7757ff2eff3471a0/images/rotary_enc.png -------------------------------------------------------------------------------- /images/schematic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crescentvenus/WALB/c28f355020981619fb61770a7757ff2eff3471a0/images/schematic.png -------------------------------------------------------------------------------- /images/test_setup2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crescentvenus/WALB/c28f355020981619fb61770a7757ff2eff3471a0/images/test_setup2.png -------------------------------------------------------------------------------- /level2.txt: -------------------------------------------------------------------------------- 1 | 0 2 | 3 3 | 6 4 | 9 5 | 12 6 | 24 7 | 30 8 | 47 -------------------------------------------------------------------------------- /menu2.txt: -------------------------------------------------------------------------------- 1 | # format of menu item: up to 20 lines 2 | # label,I/Q file,freqency,sample_rate....for I/Q file replay 3 | # label,exec scripts file name,0,0 4 | Tx-POWER,0,0,0 5 | DateTime,0,0,0 6 | CN_fix,CN1.txt,1575420000,2600000 7 | Mandal-1,Mandalay_Bay.txt,1575420000,2600000 8 | Mandal-B,Mandal-B.bin,1575420000,2600000 9 | Paris,paris.txt,1575420000,2600000 10 | Paris-B,Paris.bin,1575420000,2600000 11 | Tokyo,ichigaya.txt,1575420000,2600000 12 | ADS-B,/home/pi/scripts/ads-b.sh,0,0 13 | reboot,reboot,0,0 14 | shutdown,shutdown -h now,0,0 15 | eth,/home/pi/eth.sh,0,0 16 | wlan,/home/pi/wlan.sh,0,0 17 | -------------------------------------------------------------------------------- /python/replay.ini: -------------------------------------------------------------------------------- 1 | # [variable] has sets of a variable name and its value. 2 | # Those values can be changed by menu action. 3 | 4 | [variable] 5 | power : 0 6 | date : Now 7 | 8 | # [menu] and [menu.*] are main menu, menu items, values and actions. 9 | # [menu] or [menu.submenu.*] has selection items or values. 10 | # [menu.item.*] has a selected action. 11 | # 12 | # The title field will be showed on 1st line of the display. 13 | # The detail field will be showed on 2nd line of the display. 14 | # Its format is same as Python string's fomrat. 15 | # https://docs.python.org/2/library/string.html#format-string-syntax 16 | # Ex) {power:<3} means show the variable named power formated in the left-justified three digits. 17 | # No detail will be showed blank, selected item/value or executed command output. 18 | # The select field is one of following: 19 | # item : select a item, and action it 20 | # value : select a value, and set it to a variable in [variable] 21 | # The action field is one of following: 22 | # submenu : go down into a sub menu 23 | # command : execute a command 24 | # The item field is a list of menu items linked to [menu.item.*]. 25 | # The value field is list of values. 26 | # The variable field is a variable name in [variable]. 27 | # The submenu field is a sub menu name linked to [menu.submenu.*]. 28 | # The command field is a command string. It will be formated by Python's string. 29 | 30 | [menu] 31 | select : item 32 | item : menu.item.TxPOWER 33 | menu.item.DateTime 34 | menu.item.CN_fix 35 | menu.item.Mandalay 36 | menu.item.Paris 37 | menu.item.MOD 38 | menu.item.satellite 39 | menu.item.eth 40 | menu.item.wlan 41 | menu.item.reboot 42 | menu.item.shutdown 43 | 44 | [menu.item.TxPOWER] 45 | title : =TxPOWER 46 | detail : {power} 47 | action : submenu 48 | submenu : menu.submenu.TxPOWER 49 | 50 | [menu.submenu.TxPOWER] 51 | title : *TxPOWER 52 | select : value 53 | value : 3 6 9 12 24 30 47 54 | variable: power 55 | 56 | [menu.item.DateTime] 57 | title : =DateTime 58 | detail : {date} 59 | action : submenu 60 | submenu : menu.submenu.DateTime 61 | 62 | [menu.submenu.DateTime] 63 | title : *DateTim 64 | select : value 65 | value : Now ORG 3Y+ 3Y- 1D+ 1D- 66 | variable: date 67 | 68 | [menu.item.CN_fix] 69 | title : >CX_fix 70 | detail : {power:<3}{date} 71 | action : command 72 | command : /home/pi/sim_start.sh CN1.txt {power} 1575420000 2600000 {date} 73 | 74 | [menu.item.Mandalay] 75 | title : >Mandalay 76 | detail : {power:<3}{date} 77 | action : command 78 | command : /home/pi/sim_start.sh Mandalay_Bay.txt {power} 1575420000 2600000 {date} 79 | 80 | [menu.item.Paris] 81 | title : >Paris 82 | detail : {power:<3}{date} 83 | action : command 84 | command : /home/pi/sim_start.sh paris.txt {power} 1575420000 2600000 {date} 85 | 86 | [menu.item.MOD] 87 | title : >MOD 88 | detail : {power:<3}{date} 89 | action : command 90 | command : /home/pi/sim_start.sh ichigaya.txt {power} 1575420000 2600000 {date} 91 | 92 | [menu.item.satellite] 93 | title : >satellit 94 | detail : {power:<3}{date} 95 | action : command 96 | command : /home/pi/sim_start.sh satellite.csv {power} 1575420000 2600000 {date} 97 | 98 | [menu.item.eth] 99 | title : !eth 100 | detail : address 101 | action : command 102 | command : /home/pi/eth.sh 103 | 104 | [menu.item.wlan] 105 | title : !wlan 106 | detail : address 107 | action : command 108 | command : /home/pi/wlan.sh 109 | 110 | [menu.item.reboot] 111 | title : !reboot 112 | detail : 113 | action : command 114 | command : /bin/i2c-disp.sh -i 'reboot...'; reboot 115 | 116 | [menu.item.shutdown] 117 | title : !shutdown 118 | detail : now 119 | action : command 120 | command : /bin/i2c-disp.sh -i 'Bye!'; shutdown -h now 121 | 122 | # [cancel] is the command when the switch is long pushed. 123 | 124 | [cancel] 125 | command : /home/pi/kill_proc.sh 126 | -------------------------------------------------------------------------------- /python/replay.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:utf-8 -*- 3 | 4 | """ 5 | WALB ( Wireless Attack Launch Box ) User Interface 6 | """ 7 | 8 | __author__ = "shiracamus " 9 | __version__ = "1.0.0" 10 | __date__ = "13 June 2016" 11 | 12 | import os 13 | import sys 14 | import time 15 | import threading 16 | import subprocess as Command 17 | import traceback 18 | import wiringpi 19 | from Queue import Queue, Empty 20 | from ConfigParser import ConfigParser 21 | 22 | 23 | CONFIG_FILE = "/home/pi/replay.ini" 24 | 25 | 26 | ############################################################################### 27 | # Hardware Input / Output 28 | ############################################################################### 29 | 30 | 31 | class GPIO: # Raspberry Pi 2 GPIO 32 | """ 33 | The GPIO accesses the general purpose I/O ports. 34 | The GPIO is only one in the system, so it has only class members. 35 | """ 36 | 37 | @classmethod 38 | def setup(self): 39 | """ 40 | Setup to use the GPIO. 41 | """ 42 | wiringpi.wiringPiSetup() # setup the wiringPi library 43 | self.A = self.DigitalInputPin(0) # the rotary encorder A 44 | self.B = self.DigitalInputPin(1) # the rotary encorder B 45 | self.RED = self.LED(2) # the LED on the rotary encorder RED 46 | self.GREEN = self.LED(3) # the LED on the rotary encorder GREEN 47 | self.SW = self.DigitalInputPin(4) # the push switch on the rotary encorder 48 | self.A.pullUp() # set default level HIGH 49 | self.B.pullUp() # set default level HIGH 50 | self.SW.pullUp() # set default level HIGH 51 | 52 | 53 | class DigitalInputPin(object): 54 | """ 55 | DigitalInputPin can read the pin ether the digital level HIGH or LOW. 56 | """ 57 | 58 | def __init__(self, pin): 59 | self.pin = pin 60 | wiringpi.pinMode(pin, wiringpi.INPUT) 61 | 62 | def pullUp(self): 63 | wiringpi.pullUpDnControl(self.pin, wiringpi.PUD_UP) 64 | 65 | def onBothEdge(self, handler): 66 | wiringpi.wiringPiISR(self.pin, wiringpi.INT_EDGE_BOTH, handler) 67 | 68 | def read(self): 69 | return wiringpi.digitalRead(self.pin) 70 | 71 | 72 | class DigitalOutputPin(object): 73 | """ 74 | The DigitalOutputPin can set the pin ether the digital level HIGH or LOW. 75 | """ 76 | 77 | def __init__(self, pin): 78 | self.pin = pin 79 | wiringpi.pinMode(pin, wiringpi.OUTPUT) 80 | 81 | def on(self): 82 | wiringpi.digitalWrite(self.pin, wiringpi.HIGH) 83 | 84 | def off(self): 85 | wiringpi.digitalWrite(self.pin, wiringpi.LOW) 86 | 87 | 88 | class LED(DigitalOutputPin): 89 | """ 90 | The LED is a DititalOutputPin, it can blink itself. 91 | """ 92 | 93 | def blink(self, count): 94 | for i in xrange(count): 95 | self.on() 96 | Sleep.millis(200) 97 | self.off() 98 | Sleep.millis(200) 99 | 100 | 101 | ############################################################################### 102 | # View 103 | ############################################################################### 104 | 105 | 106 | class View: 107 | """ 108 | The View is a viewer of a model. 109 | 110 | It shows user information of the model. 111 | """ 112 | 113 | @staticmethod 114 | def display(message1, message2="", message3=""): 115 | """ 116 | Show messages on the display that able to show 2rows 8columns characters. 117 | """ 118 | Console.println("{0} {1} {2}".format(message1, message2, message3)) 119 | Command.call(['/bin/i2c-disp.sh', '-i', message1]) 120 | message2 and Command.call(['/bin/i2c-disp.sh', '-p', '0x40', message2]) 121 | message3 and Command.call(['/bin/i2c-disp.sh', '-p', '0x43', message3]) 122 | 123 | 124 | class MenuDisplay(threading.Thread): 125 | """ 126 | The MenuDisplay displays the contents of the menu that the user has operated. 127 | """ 128 | 129 | def __init__(self, menu): 130 | super(View.MenuDisplay, self).__init__() 131 | self.menu = menu 132 | self.running = threading.Event() 133 | 134 | def run(self): 135 | self.running.set() 136 | while self.running.is_set() and self.menu.waitChanged(): 137 | self.menu.clearChanged() 138 | View.display(self.menu.title, self.menu.detail) 139 | self.running.clear() 140 | 141 | def stop(self): 142 | if self.running.is_set(): 143 | self.running.clear() 144 | self.menu.setChanged() 145 | self.join() 146 | 147 | 148 | class BusyLED(threading.Thread): 149 | """ 150 | The BusyLED shows the system is busy to blink an LED. 151 | You can set 'isBusy' function that returns if the system is busy. 152 | """ 153 | 154 | def __init__(self, led): 155 | super(View.BusyLED, self).__init__() 156 | self.led = led 157 | self.running = threading.Event() 158 | # event notifier 159 | self.isBusy = lambda: False 160 | 161 | def run(self): 162 | self.running.set() 163 | while self.running.set(): 164 | if self.isBusy(): 165 | self.led.on() 166 | Sleep.millis(500) 167 | self.led.off() 168 | Sleep.millis(500) 169 | else: 170 | Sleep.millis(1000) 171 | 172 | def stop(self): 173 | if self.running.is_set(): 174 | self.running.clear() 175 | self.join() 176 | 177 | 178 | ############################################################################### 179 | # Controller 180 | ############################################################################### 181 | 182 | 183 | class Controller: 184 | """ 185 | The Controller handles user operations and modify the model. 186 | """ 187 | 188 | class PushSwitch(threading.Thread): 189 | """ 190 | A push switch device. 191 | You can set callback functions 'onPushShort', 'onPushLong', 'onReleaseShort' and 'onReleaseLong'. 192 | """ 193 | 194 | PUSHED_LONG_TIME = 2000 # The time to detect pushed long in milli seconds 195 | 196 | def __init__(self, pin): 197 | super(Controller.PushSwitch, self).__init__() 198 | self.pin = pin 199 | self.is_pushed_long = False 200 | self.running = threading.Event() 201 | # event handlers 202 | self.onPushShort = lambda: None 203 | self.onPushLong = lambda: None 204 | self.onReleasShort = lambda: None 205 | self.onReleasLong = lambda: None 206 | 207 | def run(self): 208 | self.running.set() 209 | while self.running.is_set(): 210 | if self.isPushed(): 211 | self.pushed() 212 | self.avoidChattering() 213 | self.waitToRelease() 214 | self.avoidChattering() 215 | self.released() 216 | Sleep.millis(100) 217 | 218 | def stop(self): 219 | self.running.clear() 220 | self.join() 221 | 222 | def isPushed(self): 223 | return self.pin.read() == wiringpi.LOW 224 | 225 | def avoidChattering(self): 226 | Sleep.millis(50) 227 | 228 | def waitToRelease(self): 229 | self.is_pushed_long = False 230 | pushing_time = 0 231 | while self.isPushed(): 232 | Sleep.millis(50) 233 | pushing_time += 50 234 | if not self.is_pushed_long and pushing_time >= self.PUSHED_LONG_TIME: 235 | self.is_pushed_long = True 236 | self.pushedLong() 237 | 238 | def pushed(self): 239 | Console.println("pushing...") 240 | self.onPushShort() 241 | 242 | def pushedLong(self): 243 | Console.println("pushing long...") 244 | self.onPushLong() 245 | 246 | def released(self): 247 | if self.is_pushed_long: 248 | Console.println("pushed long") 249 | self.onReleasLong() 250 | else: 251 | Console.println("pushed short") 252 | self.onReleasShort() 253 | 254 | 255 | class RotarySensor(object): 256 | """ 257 | The RotarySensor is a sensor placed inside of a RotaryEncoder. 258 | It callback specified function when the rotary encoder is rotated. 259 | """ 260 | 261 | def __init__(self, pin, callback, direction): 262 | self.pin = pin 263 | self.callback = callback 264 | self.direction = direction 265 | self.last_status = None 266 | pin.onBothEdge(lambda: self.interrupted()) 267 | 268 | def interrupted(self): 269 | current_status = self.pin.read() 270 | status_changed = self.last_status == wiringpi.LOW and current_status == wiringpi.HIGH 271 | self.last_status = current_status 272 | if status_changed: 273 | self.callback(self.direction) 274 | else: 275 | self.callback(0) 276 | 277 | 278 | class RotaryEncoder(object): 279 | """ 280 | The RotaryEncoder is a device, such as a dial, you can enter the rotation direction and rotation steps. 281 | It has 2 sensors to detect rotation direction (clockwise or counterclockwise) and rotation steps. 282 | You can set callback functions 'onRotateClockwise' and 'onRotateCounterclockwise'. 283 | """ 284 | 285 | def __init__(self, pinA, pinB): 286 | self.A = Controller.RotarySensor(pinA, self.rotate, +1) 287 | self.B = Controller.RotarySensor(pinB, self.rotate, -1) 288 | self.direction = None 289 | # event handler 290 | self.onRotateClockwise = lambda: None 291 | self.onRotateCounterclockwise = lambda: None 292 | 293 | def rotate(self, direction): 294 | """ 295 | When a sensor detects rotation, this method is callbacked. 296 | The direction argument means: +1 is sensed by sensor A, -1 is sensed by sensor B, 0 is the end to rotate. 297 | 298 | A +1: ______/~~~~~\__________/~~~~~\______ 299 | 0: 300 | B -1: ~~~\_____/~~~~~~~~~~~~~~~~\_____/~~~ 301 | clockwise counterclockwise 302 | When callbacked by A(+1) after B(-1), the rotation is clockwise. 303 | """ 304 | if direction == 0 or self.direction == 0: # unknown rotate direction 305 | pass 306 | elif direction == self.direction: # maybe it is chattering 307 | pass 308 | elif direction > self.direction: # A detected after B 309 | Console.println("clockwise") 310 | self.onRotateClockwise() 311 | elif direction < self.direction: # B detected after A 312 | Console.println("counterclockwise") 313 | self.onRotateCounterclockwise() 314 | self.direction = direction # keep last direction 315 | 316 | def start(self): 317 | pass 318 | 319 | def stop(self): 320 | pass 321 | 322 | 323 | ############################################################################### 324 | # Model 325 | ############################################################################### 326 | 327 | 328 | class Model: 329 | """ 330 | The model is data for controllers and views. 331 | """ 332 | 333 | class MenuBuilder(object): 334 | """ 335 | The MenuBuilder builds the menu to read a configuration file. 336 | """ 337 | 338 | def __init__(self, config): 339 | self.config = config 340 | 341 | def buildMainMenu(self): 342 | menu = self.build('menu', 'select') 343 | variables = dict(self.config.items('variable')) 344 | return Model.MainMenu(menu, variables) 345 | 346 | def build(self, section, option): 347 | target = self.config.get(section, option) 348 | return eval('self.build_' + target)(section) 349 | 350 | def build_item(self, section): 351 | items = tuple(self.build(item, 'action') 352 | for item in self.config.get(section, 'item').split()) 353 | return Model.Menu(items) 354 | 355 | def build_submenu(self, section): 356 | get = self.config.get 357 | title = get(section, 'title') 358 | detail = get(section, 'detail') 359 | submenu = get(section, 'submenu') 360 | item = self.build(submenu, 'select') 361 | return Model.Menu.SubMenu(title, detail, item) 362 | 363 | def build_value(self, section): 364 | get = self.config.get 365 | title = get(section, 'title') 366 | variable = get(section, 'variable') 367 | values = get(section, 'value').split() 368 | return Model.Menu.Value(title, variable, values) 369 | 370 | def build_command(self, section): 371 | get = self.config.get 372 | title = get(section, 'title') 373 | detail = get(section, 'detail') 374 | command = get(section, 'command') 375 | return Model.Menu.Command(title, detail, command) 376 | 377 | 378 | class Menu(object): 379 | """ 380 | The Menu has items and able to select an item and fire its action. 381 | """ 382 | 383 | def __init__(self, items): 384 | self.items = items 385 | self.select = 0 386 | self.submenu = None 387 | 388 | def __repr__(self): 389 | return 'Menu(%s)' % repr(self.items) 390 | 391 | @property 392 | def title(self): 393 | return self.item.title 394 | 395 | @property 396 | def detail(self): 397 | return self.item.detail 398 | 399 | @property 400 | def item(self): 401 | if self.submenu: 402 | return self.submenu.item 403 | else: 404 | return self.items[self.select] 405 | 406 | def forward(self): 407 | if self.submenu: 408 | self.submenu.forward() 409 | else: 410 | self.select = (self.select + 1) % len(self.items) 411 | 412 | def backward(self): 413 | if self.submenu: 414 | self.submenu.backward() 415 | else: 416 | self.select = (self.select - 1) % len(self.items) 417 | 418 | def action(self, variables): 419 | self.submenu, changed = self.item.action(variables) 420 | return changed 421 | 422 | 423 | class SubMenu(object): 424 | """ 425 | The SubMenu has an action and able to fire it. 426 | """ 427 | 428 | def __init__(self, title, detail, menu): 429 | self.title = title 430 | self.detail = detail 431 | self.menu = menu 432 | 433 | def __repr__(self): 434 | return 'SubMenu(%s, %s, %s)' % (repr(self.title), repr(self.detail), repr(self.menu)) 435 | 436 | def action(self, variables): 437 | return self.menu, True # go down into the sub menu, and update display 438 | 439 | 440 | class Value(object): 441 | """ 442 | The Value has values and able to select a value and set a variable it. 443 | """ 444 | 445 | def __init__(self, title, variable, items): 446 | self.title = title 447 | self.variable = variable 448 | self.items = items 449 | self.select = 0 450 | 451 | def __repr__(self): 452 | return 'Value(%s, %s, %s)' % (repr(self.title), repr(self.variable), repr(self.items)) 453 | 454 | @property 455 | def detail(self): 456 | return self.items[self.select] 457 | 458 | @property 459 | def item(self): 460 | return self 461 | 462 | def forward(self): 463 | self.select = (self.select + 1) % len(self.items) 464 | 465 | def backward(self): 466 | self.select = (self.select - 1) % len(self.items) 467 | 468 | def action(self, variables): 469 | variables[self.variable] = self.items[self.select] 470 | return None, True # back to the main menu, update display 471 | 472 | 473 | class Command(object): 474 | """ 475 | The Command has a command and able to execute it. 476 | """ 477 | 478 | def __init__(self, title, detail, command): 479 | self.title = title 480 | self.detail = detail 481 | self.command = command 482 | 483 | def __repr__(self): 484 | return 'Command(%s, %s, %s)' % (repr(self.title), repr(self.detail), repr(self.command)) 485 | 486 | def action(self, variables): 487 | Command.call(self.command.format(**variables), shell=True) 488 | return None, False # back to the main menu, no update dispaly for staying the command output 489 | 490 | 491 | class MainMenu(object): 492 | """ 493 | The MainMenu has a menu and able to notice changes its contents to display. 494 | """ 495 | 496 | def __init__(self, menu, variables): 497 | self.menu = menu 498 | self.variables = variables 499 | self.changed = threading.Event() 500 | self.setChanged() 501 | 502 | def __repr__(self): 503 | return 'MainMenu(%s, %s)' % (repr(self.menu), repr(self.variables)) 504 | 505 | @property 506 | def title(self): 507 | return self.menu.title 508 | 509 | @property 510 | def detail(self): 511 | return self.menu.detail.format(**self.variables) 512 | 513 | def forward(self): 514 | self.menu.forward() 515 | self.setChanged() 516 | 517 | def backward(self): 518 | self.menu.backward() 519 | self.setChanged() 520 | 521 | def action(self): 522 | if self.menu.action(self.variables): 523 | self.setChanged() 524 | 525 | def setChanged(self): 526 | self.changed.set() 527 | 528 | def clearChanged(self): 529 | self.changed.clear() 530 | 531 | def isChanged(self): 532 | return self.changed.is_set() 533 | 534 | def waitChanged(self): 535 | return self.changed.wait() 536 | 537 | 538 | ############################################################################### 539 | # System / Library 540 | ############################################################################### 541 | 542 | 543 | class Console(object): 544 | """ 545 | The Console shows the messages on a console or terminal. 546 | """ 547 | 548 | @classmethod 549 | def println(self, message): 550 | if isinstance(message, (tuple, list)): 551 | print ' '.join(map(str, message)) 552 | else: 553 | print str(message) 554 | 555 | 556 | class Sleep: 557 | """ 558 | Sleep a while. 559 | """ 560 | 561 | @staticmethod 562 | def days(d): 563 | Sleep.hours(d * 24) 564 | 565 | @staticmethod 566 | def hours(h): 567 | Sleep.minutes(h * 60) 568 | 569 | @staticmethod 570 | def minutes(m): 571 | Sleep.seconds(m * 60) 572 | 573 | @staticmethod 574 | def seconds(s): 575 | Sleep.millis(s * 1000) 576 | 577 | @staticmethod 578 | def millis(ms): 579 | wiringpi.delay(ms) 580 | 581 | 582 | ############################################################################### 583 | # main 584 | ############################################################################### 585 | 586 | 587 | def main(): 588 | 589 | # setup I/O 590 | GPIO.setup() 591 | 592 | # show startup message 593 | if True: 594 | Console.println("Config file: " + CONFIG_FILE) 595 | View.display('replay4', 'python') 596 | GPIO.RED.blink(2) 597 | GPIO.GREEN.blink(2) 598 | 599 | # read config 600 | config = ConfigParser() 601 | config.read(CONFIG_FILE) 602 | 603 | # build model 604 | menu = Model.MenuBuilder(config).buildMainMenu() 605 | 606 | # build view 607 | display = View.MenuDisplay(menu) 608 | 609 | transmit_indicator = View.BusyLED(GPIO.RED) 610 | transmit_indicator.isBusy = lambda: Command.call(["/home/pi/stat.sh"]) == 1 # transmitting now 611 | 612 | # build controller 613 | rotary_encoder = Controller.RotaryEncoder(GPIO.A, GPIO.B) 614 | rotary_encoder.onRotateClockwise = menu.forward 615 | rotary_encoder.onRotateCounterclockwise = menu.backward 616 | 617 | push_switch = Controller.PushSwitch(GPIO.SW) 618 | push_switch.onPushShort = lambda: (GPIO.GREEN.on()) 619 | push_switch.onReleasShort = lambda: (GPIO.GREEN.off(), menu.action()) 620 | push_switch.onPushLong = lambda: (GPIO.GREEN.off(), GPIO.RED.on()) 621 | push_switch.onReleasLong = lambda: (GPIO.RED.off(), Command.call(config.get('cancel', 'command'), shell=True)) 622 | 623 | # start 624 | display.start() 625 | transmit_indicator.start() 626 | rotary_encoder.start() 627 | push_switch.start() 628 | 629 | try: 630 | # main loop 631 | while True: 632 | Sleep.seconds(1) 633 | finally: 634 | # stop 635 | push_switch.stop() 636 | rotary_encoder.stop() 637 | transmit_indicator.stop() 638 | display.stop() 639 | 640 | 641 | if __name__ == '__main__': 642 | main() 643 | -------------------------------------------------------------------------------- /replay2.c: -------------------------------------------------------------------------------- 1 | /* 2 | gcc replay2.c -I/usr/local/include -L/usr/local/lib -lwiringPi -o replay2 3 | 4 | SW_level_stat 0 ...main menu 5 | SW_level_stat 1 ...Level Set 0,3,6,9,12.... 6 | SW_level_stat 2 ...Date & Time Set Now,ORG,Y3+,D1-,.... 7 | 8 | */ 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #define NOT_FOUND -1 17 | #define MAX_LINE 200 18 | #define MAX_CHAR 200 19 | 20 | /* Raspberry Pi 2 GPIO */ 21 | #define rA 0 // rotary encorder A 22 | #define rB 1 // rotary encorder B 23 | #define rRED 2 // LED on rotary encorder RED 24 | #define rGREEN 3 // LED on rotary encorder GREEN 25 | #define rSW 4 // Push SW on rotary encorder 26 | 27 | int a, b; 28 | int p_a, p_b; 29 | int int_a=0, int_b=0, int_disp = 0; 30 | int num=0, num2=0, num3=0,last_tx=1; 31 | int SW_long = 500; 32 | int SW_level_stat =0; 33 | 34 | int now =0; // timer to check gps-sdr-sim process status 35 | int prev = 0; 36 | time_t rtime; 37 | 38 | FILE *fp; 39 | char *menu_name, *file_name, *freq, *s_rate, *date_time, *power; 40 | char *p; 41 | char s[MAX_CHAR]; 42 | /* M for menu, L for Level, D for Date&Time, P for Position */ 43 | int Mmax,Lmax,Dmax,Pmax; // max number of lines on power level setting 44 | char *scripts = "scripts/"; 45 | char *home = "/home/pi/"; 46 | char *Mname = "menu2.txt"; 47 | char *Lname = "level2.txt"; 48 | char *Dname = "date2.txt"; 49 | 50 | char path[50]; 51 | char Mbuf[MAX_LINE][MAX_CHAR]; 52 | char Lbuf[MAX_LINE][MAX_CHAR]; 53 | char Dbuf[MAX_LINE][MAX_CHAR]; 54 | 55 | char cmd[50]="i2c-disp.sh -i "; 56 | char cmd2[50]="i2c-disp.sh -p 0x40 "; 57 | char cmd3[50]="i2c-disp.sh -p 0x43 "; 58 | 59 | int Trim(char *s) { 60 | int i; 61 | int count = 0; 62 | 63 | if ( s == NULL ) { /* yes */ 64 | return -1; 65 | } 66 | i = strlen(s); 67 | while ( --i >= 0 && s[i] == '\n' ) count++; 68 | s[i+1] = '\0'; 69 | i = 0; 70 | while ( s[i] != '\0' && s[i] == '\n' ) i++; 71 | strcpy(s, &s[i]); 72 | 73 | return i + count; 74 | } 75 | 76 | int strpos(char *haystack, char *needle) 77 | { 78 | char *p = strstr(haystack, needle); 79 | if (p) 80 | return p - haystack; 81 | return NOT_FOUND; 82 | } 83 | 84 | void proc(int count){ 85 | char str[MAX_CHAR]; 86 | char tmp[MAX_CHAR]; 87 | if(int_disp==0){ 88 | int_disp=1; 89 | switch (SW_level_stat){ 90 | case 0: 91 | num+=count; 92 | if(num < 0) num=Mmax-1; 93 | num = num % Mmax; 94 | /* Get current menu item */ 95 | /* char *menu_name, *file_name, *freq, *s_rate */ 96 | strcpy(tmp,Mbuf[num]); 97 | menu_name=strtok(tmp,","); 98 | file_name=strtok(NULL,","); 99 | freq=strtok(NULL,","); 100 | s_rate=strtok(NULL,","); 101 | strcpy(str,path); 102 | strcat(str,cmd); 103 | strcat(str, menu_name); 104 | system(str); // Display 1st line on LCD 105 | if(atoi(s_rate)!=0 || num==0){ // Power 106 | strcpy(str,path); 107 | strcat(str,cmd2); // Display 2nd line on LCD 108 | strcat(str,Lbuf[num2]); 109 | system(str); 110 | strcpy(str,path); 111 | strcat(str,cmd3); 112 | strcat(str,Dbuf[num3]); 113 | system(str); 114 | } 115 | if( num==1 ){ 116 | strcpy(str,path); 117 | strcat(str,cmd2); // Display 2nd line on LCD 118 | strcat(str,Dbuf[num3]); // Date&Time 119 | system(str); 120 | } 121 | break; 122 | case 1: // Sub menu --- Level set 123 | num2=disp_sub(count, num2, Lmax,"*TxPower", Lbuf); 124 | break; 125 | case 2: 126 | num3=disp_sub(count, num3, Dmax,"*DateTime", Dbuf); 127 | break; 128 | } 129 | int_disp=0; 130 | } 131 | } 132 | 133 | int disp_sub(int count, int pos, int max, char *msg1, char msg2[][MAX_CHAR]){ 134 | char str[MAX_CHAR]; 135 | 136 | pos+=count; 137 | if(pos < 0) pos=max-1; 138 | pos = pos % max; 139 | strcpy(str,cmd); 140 | strcat(str, msg1); 141 | system(str); // Display 1st line 142 | strcpy(str,cmd2); 143 | strcat(str,msg2[pos]); 144 | system(str); // Display 2nd line 145 | return pos; 146 | } 147 | 148 | void click_a(void){ 149 | int _a; 150 | if (int_a ==0 ){ 151 | int_a=1; 152 | _a = digitalRead(rA); 153 | if(_a != a){ 154 | if(a == 1){ 155 | p_a = 1; 156 | }else{ 157 | p_a = 0; 158 | } 159 | a = _a; 160 | if(a == 0 && b == 0 && p_a == 1 && p_b == 1){ 161 | proc(1); 162 | } 163 | } 164 | int_a=0; 165 | } 166 | } 167 | void click_b(void){ 168 | int _b; 169 | if ( int_b == 0){ 170 | int_b=1; 171 | _b = digitalRead(rB); 172 | if(_b != b){ 173 | if(b == 1){ 174 | p_b = 1; 175 | }else{ 176 | p_b = 0; 177 | } 178 | b = _b; 179 | if(a == 0 && b == 0 && p_a == 1 && p_b == 1){ 180 | proc(-1); 181 | } 182 | } 183 | int_b=0; 184 | } 185 | } 186 | 187 | void flashLED(int color, int counts){ 188 | int i; 189 | for(i=0;i0 || strpos(file_name,".csv")>0){ 339 | strcat(str,"sim_start.sh ");// generate rela-time I/Q data 340 | } else { 341 | strcat(str,"transmit2.sh "); // Use pre-generated I/Q data 342 | // Trim(str); 343 | // strcat(str,file_name); // Replay file name 344 | } 345 | Trim(str); strcat(str,file_name); // Replay file name 346 | Trim(str); strcat(str," "); 347 | strcpy(str1,Lbuf[num2]); // Replay POWER 348 | Trim(str1); 349 | strcat(str,str1); // transmit2.sh file_name POWER 350 | strcat(str," "); strcat(str,freq); 351 | strcat(str," "); 352 | Trim(s_rate); strcat(str,s_rate); 353 | strcat(str," ");strcat(str,Dbuf[num3]); 354 | strcat(str,"&"); // transmit2.sh file_name POWER& 355 | Trim(str); last_tx=num; 356 | // printf("\n[Tx shell cmd:%s]\n",str); 357 | system(str); // 実行 358 | } else { 359 | system(file_name); // 単純なshell実行 360 | } 361 | break; 362 | } 363 | while (digitalRead(rSW)==0) { // Waite until SW pressed 364 | usleep(1000); 365 | n++; 366 | if ( n > SW_long ) digitalWrite(rRED,1); 367 | } 368 | usleep(200000); // Avoid chattering 369 | } else { 370 | digitalWrite(rGREEN,0); // Now SW released, put off LED 371 | } 372 | 373 | if (n > SW_long ){ // KILL process when SW pushed long duration 374 | if(SW_level_stat==0){ 375 | system("/home/pi/kill_proc.sh"); 376 | digitalWrite(rRED,1); 377 | usleep(500000); 378 | digitalWrite(rRED,0); 379 | n=0; 380 | } else { 381 | SW_level_stat=0; 382 | proc(0); 383 | } 384 | } 385 | now = millis(); 386 | if(( now - prev ) > 1000){ // LED update while Tx working 387 | tx=tx_blink(tx); 388 | prev = now; 389 | } 390 | 391 | } 392 | } 393 | -------------------------------------------------------------------------------- /scritps/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crescentvenus/WALB/c28f355020981619fb61770a7757ff2eff3471a0/scritps/.gitkeep -------------------------------------------------------------------------------- /scritps/ads-b.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | PH=/home/pi/ADS-B_signal_generator 3 | PID=`ps ax | grep hackrf_transfer | grep -v grep | awk '{print $1}'` 4 | if test -z $PID ; then 5 | $PH/ads-b_gen0 -i $PH/aug02.raw -m 1 -o /tmp/fifo >/dev/null& 6 | hackrf_transfer -f 1090000000 -s 2000000 -x 6 -t /tmp/fifo >/dev/null& 7 | fi 8 | -------------------------------------------------------------------------------- /scritps/eth.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ipaddr=`LANG=C ifconfig eth0 |grep "inet addr" |awk {'print $2'} |cut -f2 -d:` 3 | addr_h=`echo ${ipaddr}|cut -f1,2 -d.` 4 | addr_l=`echo ${ipaddr}|cut -f3,4 -d.` 5 | 6 | case "$ipaddr" in 7 | "") 8 | i2c-disp.sh -i "none IP" 9 | ;; 10 | *) 11 | i2c-disp.sh -i ${addr_h} 12 | i2c-disp.sh . 13 | i2c-disp.sh -p 0x40 ${addr_l} 14 | ;; 15 | esac -------------------------------------------------------------------------------- /scritps/i2c-disp.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | function usage { 3 | echo "Usage: $0 [-ic] [-p pos] message" > /dev/stderr; 4 | exit 1 5 | } 6 | [ $# = 0 ] && usage 7 | 8 | while getopts "icp:" flag; do 9 | case $flag in 10 | \?) usage ;; 11 | i) i2cset -y 1 0x3e 0 0x38 0x39 0x14 0x70 0x56 0x6c i 12 | sleep 0.25 13 | i2cset -y 1 0x3e 0 0x0c 0x01 0x06 i 14 | sleep 0.05 15 | ;; 16 | c) i2cset -y 1 0x3e 0 0x01 ;; 17 | p) i2cset -y 1 0x3e 0 $((OPTARG+128)) ;; 18 | esac 19 | done 20 | shift $((OPTIND-1)) 21 | [ $# = 0 ] && exit 22 | 23 | LANG=C 24 | MSG=`echo -n "$1" | perl -pe '$_=join" ",map{ord }split//'` 25 | #echo $MSG 26 | i2cset -y 1 0x3e 0x40 $MSG i -------------------------------------------------------------------------------- /scritps/kill_proc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | PID=`ps ax | grep gps-sdr-sim | grep -v grep | awk '{print $1}'` 3 | if test -z $PID ; then 4 | echo "No such a process" 5 | else 6 | echo $PID 7 | kill -6 $PID 8 | fi 9 | PID=`ps ax | grep hackrf | grep -v grep | awk '{print $1}'` 10 | if test -z $PID ; then 11 | echo "No such a process" 12 | else 13 | echo $PID 14 | kill -6 $PID 15 | fi -------------------------------------------------------------------------------- /scritps/sim_start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | PH=/home/pi/IQ-files 3 | DIR=/home/pi/gps-sdr-sim 4 | FILE=$PH/$1 5 | POWER=$2 6 | FREQ=$3 7 | SAMPLE=$4 8 | DATE=$5 9 | N_SAT=8 10 | 11 | PID=`ps ax | grep hackrf_transfer | grep -v grep | awk '{print $1}'` 12 | if test -z $PID ; then 13 | if [ $DATE = 'Now' ]; then 14 | DATE=-T`LANG=C; date --date "18 sec" +%Y/%m/%d,%X` 15 | elif [ $DATE = 'ORG' ]; then 16 | DATE= 17 | elif [ $DATE = '3Y+' ]; then 18 | DATE=-T`LANG=C; date --date "3 year 18 sec" +%Y/%m/%d,%X` 19 | elif [ $DATE = '3Y-' ]; then 20 | DATE=-T`LANG=C; date --date "3 year ago 18 sec" +%Y/%m/%d,%X` 21 | elif [ $DATE = '1D+' ]; then 22 | DATE=-T`LANG=C; date --date "1 day 18 sec" +%Y/%m/%d,%X` 23 | elif [ $DATE = '1D-' ]; then 24 | DATE=-T`LANG=C; date --date "1 day ago 18 sec" +%Y/%m/%d,%X` 25 | fi 26 | echo $FILE, $POWER, $FREQ, $SAMPLE,$DATE 27 | LINES=`wc $FILE -l | /usr/bin/awk '{print $1}'` 28 | if [ $LINES = 1 ]; then 29 | LOCATION=`cat $FILE` 30 | echo "Fix Location" 31 | $DIR/gps-sdr-sim -e $DIR/brdc3640.15n -l $LOCATION -b8 -n $N_SAT -o $DIR/fifo $DATE& 32 | elif [ `echo $FILE | grep csv` ]; then 33 | echo "csv" 34 | cat $FILE | $DIR/gps-sdr-sim -e $DIR/brdc3640.15n -u- -b8 -n $N_SAT -o $DIR/fifo $DATE& 35 | elif [ ` echo $FILE | grep txt` ]; then 36 | echo "NMEA" 37 | cat $FILE | $DIR/gps-sdr-sim -e $DIR/brdc3640.15n -g- -b8 -n $N_SAT -o $DIR/fifo $DATE& 38 | fi 39 | 40 | /usr/l 41 | -------------------------------------------------------------------------------- /scritps/stat.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | STAT=`ps ax | grep hackrf | grep -v grep` 3 | #echo $STAT 4 | if [ -n "$STAT" ]; then 5 | RET=1 6 | else 7 | RET=0 8 | fi 9 | return $RET -------------------------------------------------------------------------------- /scritps/wlan.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ipaddr=`LANG=C ifconfig wlan0 |grep "inet addr" |awk {'print $2'} |cut -f2 -d:` 3 | addr_h=`echo ${ipaddr}|cut -f1,2 -d.` 4 | addr_l=`echo ${ipaddr}|cut -f3,4 -d.` 5 | 6 | case "$ipaddr" in 7 | "") 8 | i2c-disp.sh -i "none IP" 9 | ;; 10 | *) 11 | i2c-disp.sh -i ${addr_h} 12 | i2c-disp.sh . 13 | i2c-disp.sh -p 0x40 ${addr_l} 14 | ;; 15 | esac -------------------------------------------------------------------------------- /webui/LatLon.css: -------------------------------------------------------------------------------- 1 | * { 2 | font-size: 12px; 3 | padding: 0; 4 | margin: 0; 5 | } 6 | body{ 7 | overflow: hidden; 8 | } 9 | .fluid{ 10 | width: 100%; 11 | paddin: 0; 12 | margin: 0; 13 | } 14 | .align-left{ 15 | text-align: left; 16 | } 17 | .align-right{ 18 | text-align: right; 19 | } 20 | .inline-group{ 21 | float: left; 22 | } 23 | .container{ 24 | padding: 20px; 25 | } 26 | .form-control{ 27 | font-size: 12px; 28 | padding:2px 4px; 29 | margin: 2px; 30 | width: 60px; 31 | } 32 | .form-button{ 33 | width: 60px; 34 | } 35 | .text-red{ 36 | color: #ee0000 !important; 37 | } 38 | .text-bold{ 39 | font-weight: bold !important;; 40 | } 41 | -------------------------------------------------------------------------------- /webui/LatLon.ini.php: -------------------------------------------------------------------------------- 1 | 9 | -------------------------------------------------------------------------------- /webui/LatLon.js: -------------------------------------------------------------------------------- 1 | function initMap() { 2 | // マップの初期化 3 | map = new google.maps.Map(document.getElementById('map'), _googleMapInitSetting); 4 | 5 | // クリックイベントを追加 6 | //map.addListener('click', function(e) { 7 | // getClickLatLng(e.latLng, map); 8 | //}); 9 | map.addListener('mouseup', function(e) { 10 | getClickLatLng(e.latLng, map); 11 | }); 12 | 13 | // 初期位置にマーカーを設置 14 | var latitude = document.getElementById('lat').value; 15 | var longitude = document.getElementById('lng').value; 16 | var myLatlng = new google.maps.LatLng(latitude, longitude); 17 | var marker = new google.maps.Marker({ 18 | position: myLatlng, 19 | map: map, 20 | draggable:true 21 | }); 22 | google.maps.event.trigger(map, 'resize'); 23 | } 24 | 25 | function getClickLatLng(lat_lng, map) { 26 | // 座標を表示 27 | document.getElementById('lat').value = Math.round(lat_lng.lat()*100000)/100000; 28 | document.getElementById('lng').value = Math.round(lat_lng.lng()*100000)/100000; 29 | document.getElementById('zoom').value = map.getZoom(); 30 | 31 | // 表示した座標をサーバに送信 32 | sendLocation(_scriptUrl,'#loc'); 33 | 34 | // マーカーを設置 35 | var marker = new google.maps.Marker({ 36 | position: lat_lng, 37 | map: map, 38 | draggable:true 39 | }); 40 | 41 | // 座標の中心をずらす 42 | // http://syncer.jp/google-maps-javascript-api-matome/map/method/panTo/ 43 | map.panTo(lat_lng); 44 | google.maps.event.trigger(map, 'resize'); 45 | } 46 | 47 | function sendLocation(hostUrl, formId){ 48 | var bSuccess = true; 49 | var params = $(formId).serialize(); 50 | jQuery.ajax({ 51 | url: hostUrl, 52 | type:'POST', 53 | dataType: 'json', 54 | data : params, 55 | timeout: 10000, 56 | success: function(data) { 57 | bSuccess = true; 58 | }, 59 | error: function(XMLHttpRequest, textStatus, errorThrown) { 60 | bSuccess = false; 61 | } 62 | }); 63 | return bSuccess; 64 | } 65 | 66 | function panCenter(){ 67 | var w = parseInt(jQuery('#settings').css('width')); 68 | var th = parseInt(jQuery('#settings').css('height')); 69 | var h = window.innerHeight; 70 | jQuery('#map').css('width', '100%'); 71 | jQuery('#map').css('height', h-th-40); //container paddin 20px 72 | var latitude = parseFloat(document.getElementById('lat').value); 73 | var longitude = parseFloat(document.getElementById('lng').value); 74 | var myLatlng = new google.maps.LatLng(latitude, longitude); 75 | google.maps.event.trigger(map, 'resize'); 76 | map.panTo(myLatlng); 77 | } 78 | 79 | jQuery(window).load(function(){ 80 | panCenter(); 81 | }); 82 | 83 | jQuery(window).resize(function(){ 84 | panCenter(); 85 | }); 86 | -------------------------------------------------------------------------------- /webui/LatLon.php: -------------------------------------------------------------------------------- 1 | 40, 39 | "zoom" => 13, 40 | "latitude" => 36.090725, 41 | "longitude" => -115.175342, 42 | ]; 43 | // write initial location to file 44 | $lat = round($settings['latitude'], 6); 45 | $lng = round($settings['longitude'], 6); 46 | $data = "{$lat},{$lng},100,{$settings['zoom']},{$settings['speed']}\n"; 47 | if ($fp = fopen($location_file, 'c')) { 48 | flock($fp, LOCK_EX); 49 | ftruncate($fp, 0); 50 | fputs($fp, $data); 51 | flock($fp, LOCK_UN); 52 | fclose($fp); 53 | } 54 | } 55 | if(isset($_POST['stop'])){ 56 | exec("$cwd/start2.php stop "); 57 | } 58 | if(isset($_POST['start'])){ 59 | exec("$cwd/start2.php start"); 60 | } 61 | if(isset($_POST['latitude']) && isset($_POST['longitude'])){ 62 | $ret = "true"; 63 | $zoom = $_POST['zoom']; 64 | $speed=$_POST['speed']; 65 | $lat = $_POST['latitude']; 66 | $lng = $_POST['longitude']; 67 | $lat = round($lat, 6); 68 | $lng = round($lng, 6); 69 | $data = "{$lat},{$lng},100,{$zoom},{$speed}\n"; 70 | 71 | if ($fp = fopen($location_file, 'c')) { 72 | flock($fp, LOCK_EX); 73 | ftruncate($fp, 0); 74 | fputs($fp, $data); 75 | flock($fp, LOCK_UN); 76 | fclose($fp); 77 | }else{ 78 | $ret = "false"; 79 | } 80 | 81 | echo '{"result":'.$ret.'}'; 82 | return; 83 | } 84 | 85 | $template = new Template(); 86 | $template->show("LatLon.tmpl.php", compact('title','settings')); 87 | -------------------------------------------------------------------------------- /webui/LatLon.tmpl.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <?php echo $title ?> 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 45 |
17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 | 33 | 34 | 38 |
39 |
40 |
41 | 42 | 43 |
44 |
46 |
47 | 48 | 49 | 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /webui/README.md: -------------------------------------------------------------------------------- 1 | * Copy webui/* to youre WWW server. (ie. /var/wwww/html/webui ) 2 | * Add google API_KEY (At line #63 in LatLon.tmpl.php) 3 | * Change all of the file owner to match HTTP server's process owner. (ie. www-data:www-data) 4 | * In start2.php make sure below.
5 | gps-sdr-sim is in $DIR.
6 | $BRDC file is in $DIR.
7 | 8 | * Clicked location on the map will be written to file LatLon.txt by LatLon.php. (i.e. 35.88258,139.49264,100,14,400) 9 | * Script smooth2.php will interpolate from previous location to just clicked location and write it to "/tmp/LatLon.txt" every 1/10 second. 10 | * Enhanced gps-sdr-sim reads the file "/tmp/LatLon.txt" and generate I/Q signal. 11 | -------------------------------------------------------------------------------- /webui/kill_proc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | for proc in smooth2.php hackrf gps-sdr-sim 3 | do 4 | PID=`ps ax | grep $proc | grep -v "sh -c " | grep -v grep | awk '{print $1}'` 5 | if test -z $PID ; then 6 | echo "No such a process" 7 | else 8 | echo $PID 9 | kill -6 $PID 10 | fi 11 | done 12 | -------------------------------------------------------------------------------- /webui/smooth2.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/php 2 | $prev) { // New pointed Lat/Lon...speed 41 | echo "$dat,$prev\n"; 42 | if(isset($buf)) { 43 | $tmp=explode(",",$buf[$ptr]); 44 | $x0=$tmp[0];$y0=$tmp[1]; 45 | } 46 | $s=explode(",",$dat); 47 | $x=$s[0];$y=$s[1];$z=$s[2]; 48 | $max_v=8*trim($t[4]); 49 | $max_deg_tic=$dt*$Vdeg*$max_v*1000/3600; 50 | $offset = $max_deg_tic * 0.5; 51 | // printf("$dat, MaxV:$max_v,Max_dev_tic:$max_deg_tic\n"); 52 | $dx=($x0-$x); // dintance to next position [deg] 53 | $dy=($y0-$y); 54 | $dx_s=($dx>0)?1:-1; 55 | $dy_s=($dy>0)?1:-1; 56 | $v0=0;$v=0;$tic=0;$nx=$x0;$ny=$y0;$i=0; 57 | $t=$dt; // update every 1/10 sec. 58 | if(abs($dx)>abs($dy)){ 59 | if($dx!=0){ 60 | $sc=abs($dy/$dx); 61 | while(abs($x-$nx)>$offset){ 62 | $v=($v>=$max_deg_tic)?$max_deg_tic:$v0+$MAX_G*$t*$Vdeg; 63 | $v0=$v; 64 | $dxl=$v*$t*$dx_s; 65 | $dyl=$v*$t*$dy_s; 66 | $nx=$nx-$dxl; 67 | $ny=$ny-$dyl*$sc; 68 | if(abs($y-$ny)<$offset) $ny=$y; 69 | $buf[$i]=$nx.",".$ny.",100"; 70 | $i++; 71 | if($i>$MAX_PTR) break; 72 | } 73 | } 74 | } else { 75 | if($dy!=0){ 76 | $sc=abs($dx/$dy); 77 | while(abs($y-$ny)>$offset){ 78 | $v=($v>=$max_deg_tic)?$max_deg_tic:$v0+$MAX_G*$t*$Vdeg; 79 | $v0=$v; 80 | $dxl=$v*$t*$dx_s; 81 | $dyl=$v*$t*$dy_s; 82 | $nx=$nx-$dxl*$sc; 83 | $ny=$ny-$dyl; 84 | if(abs($x-$nx)<$offset) $nx=$x; 85 | $buf[$i]=$nx.",".$ny.",100"; 86 | $i++; 87 | if($i>$MAX_PTR) break; 88 | } 89 | } 90 | } 91 | echo "Number of points:$i\n"; 92 | if($i>$MAX_PTR){ // Change location without interpolation when distance is too far away. 93 | unset($buf); 94 | $prev=$dat; 95 | update($dat,$current_pos); 96 | } else { 97 | $buf[$i++]=$dat; 98 | $ptr_max=$i; 99 | $ptr=0;$prev=$dat; 100 | $x0=$x; $y0=$y; $z0=$z; 101 | if (isset($buf)) update($buf[$ptr++],$current_pos); 102 | } 103 | } else { // No current location changed 104 | if(isset($buf) && ($ptr<$ptr_max)){ 105 | update($buf[$ptr],$current_pos); 106 | $ptr++; 107 | } else { 108 | unset($buf); 109 | } 110 | } 111 | usleep($SLEEP); 112 | } 113 | ?> 114 | -------------------------------------------------------------------------------- /webui/start2.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/php 2 | /dev/null &"; 22 | exec($cmd2); 23 | sleep(1); 24 | exec("$CWD/smooth2.php >/dev/null &"); 25 | $cmd1="/usr/local/bin/hackrf_transfer -t $FIFO -f $FREQ -s $SAMPLE -x 0 >/dev/null &"; 26 | exec($cmd1); 27 | } 28 | ?> 29 | --------------------------------------------------------------------------------