Skip to content

Commit

Permalink
Merge branch 'fosatech:main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
PWRxPSYCHO authored Dec 26, 2023
2 parents 156ecdc + b016bf3 commit 0163362
Show file tree
Hide file tree
Showing 13 changed files with 937 additions and 196 deletions.
78 changes: 48 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@

# STK Sweep Setup Instructions

This README provides detailed instructions on how to set up the STK Sweep server, as well as usage documentation. Follow these steps to get started.
**Version 0.2.1**

## Prerequisites

Before proceeding, ensure you have the following installed on your system:
- Python 3
- `pip` + `virtualenv`
- `rtl-sdr` package, including `rtl_tcp` and `rtl_power`
- `hackrf_sweep` for the [HackRF](https://github.com/greatscottgadgets/hackrf)

## Installation and Setup

Before starting STK Sweep, make sure that you have the backend tools like rtl_tcp, rtl_power, or hackrf_sweep installed and updated to the most current version. If you're on Windoze, make sure that you have these in your system PATH. Instuctions for all this can be found with some quick Googleing.

1. **Clone the Source Code**
```bash
git clone https://github.com/fosatech/STK-Sweep.git
Expand Down Expand Up @@ -41,7 +44,7 @@ Before proceeding, ensure you have the following installed on your system:
```bash
python stk_sweep.py
```
Note: this currently starts the Flask development server.
Note: this currently starts the Flask development server. Proper WSGI server support is coming soon.

## Docker

Expand All @@ -58,24 +61,49 @@ docker compose run --publish 5000:5000 stk_sweep

After completing the setup, the Flask application will be running on your local server. You can access it via `localhost:5000/rtl_data`.

1. Enter the desired wideband scan range and bin size and start the scan.
1. Open the settings window, enter the desired wideband scan range and bin size, and start the scan.

2. Start the `rtl_tcp` server with the desired port and IP, and connect to it with your SDR software.

3. `ctrl + left click` on a frequency and STK Sweep will automatically tune your 2nd SDR to it.
3. Select your desired rtl-tcp server instance by clicking on the current frequency, then `ctrl + left click` on the wideband window and STK Sweep will automatically tune your SDR to it.

## Settings and Configuration

**Wideband Settings:**

Enter the freqency range, gain, and bin size.

- Gain should be between 0-50, or "automatic".
- The bin size is in Hz, and sets the frequency range that each pixel covers. Lower bin size, more resolution.
- Gain for the rtl-sdr should be between 0-50, or "automatic".
- Gain for the hackrf should be between 0-40.
- The bin size is in kHz, and sets the frequency range that each pixel covers. Lower bin size, more resolution.
- The reccomended bin size is 0.12% of your bandwidth. A simple way to calculate this is `<bw in MHz> * 0.12`. This will give you the optimal bin size in kHz.
- Note: the device ID does not currently correlate to any specific device. This will be fixed soon.

![wideband settings](readme/wideband-settings.png)


**RTL TCP Settings:**

This is for creating new instaces of the backend `rtl_tcp` server. `STK Sweep` has a TCP proxy on the backend in order to be able to update the center frequency of the 2nd RTL-SDR dongle.

- **IP:** The IP address your external SDR software will connect to.
- **Port:** The port your external SDR software will conenct to.
- **Gain:** Gain for the rtl-sdr. Can be set from 0 - 50.
- **Client Port:** The port that the backend proxy server and the rtl-tcp instance will use to talk to each other.
- **Device:** The device ID for rtl-tcp to use. Change this if you're using multiple rtl-sdr's.
- **Color:** Sets the display color.

![rtl_tcp settings](readme/rtl-tcp-settings.png)

![sdrpp-example](readme/sdrpp-example.png)

Once you've created and configured your rtl-tcp instance, use the Start/Stop buttons to start and stop the server. To chage the center frequency that your device is tuned to, click on the frequency display for your desired instance, and you will see it selected. Then simply `ctrl + click` anywhere on the wideband to tune your rtl-tcp instance to that frequency. You will see an overlay pop up on the wideband waterfall that shows the current area your SDR is looking at.

![rtl-tcp-instance](readme/rtl-tcp-instance.png)

![tuned-instance](readme/tuned-instance-example.png)


**Waterfall Display Settings:**

This sets the color profile for each new row of the waterfall. Future versions will update the entire waterfall color scheme.
Expand All @@ -84,12 +112,6 @@ The `Activity Threshold` slider sets the threshold for the activity bar.

![waterfall display settings](readme/waterfall-settings.png)

**RTL TCP Settings:**

This is for starting the backend `rtl_tcp` server. `STK Sweep` has a TCP proxy on the backend in order to be able to update the center frequency of the 2nd RTL-SDR dongle.

![rtl_tcp settings](readme/rtl-tcp-settings.png)


## Support

Expand All @@ -101,34 +123,30 @@ Contributions to the SDR-STK project are welcome. Please read the `CONTRIBUTING.

## To-Do

**Coming Soon:**
- Save scan files to disk
- Multiple concurrent widebands
- Update info from backend when page refreshes
- Type in center frequency for live SDR's
- Proper mobile browser support
- Zoom and scroll relative to page center/mouse cursor
- Create custom frequency plans
- **[ redacted ]**

**Bug Fixes:**
- ~~Frontend breaks with really wide scan~~
- ~~Buttons don't turn red on `stop`~~
- ~~Exception catches on backend~~
- Large `bin` size breaks `drawRow()` in `rtlDataDisplay.js`
- Implement proper server like `gunicorn` without `rtl_tcp` proxy lagging
- ~~Remove old requirements~~
- Detect when scan or tcp server is stoppen on backend
- ~~Properly kill tcp proxy before initial connection~~
- Fix broken sliders on some browsers (Brave, Opera)
- Wideband and tcp proxy instances don't always shut down properly
- ~~Fix broken sliders on some browsers (Brave, Opera)~~

**Aditional Features:**
- Convince entire population of earth to use Linux || Add Windoze support
- Convince entire population of earth to use Linux || Test Windoze support
- Custom wideband backend
- Zoom and scroll relative to page center/mouse cursor
- Save scan files to disk
- Add documentation
- ~~Add license~~
- Add usage docs
- Add more `rtl_power` options
- Add page settings
- Update entire waterfall colors
- Proper waterfall schema
- Multiple waterfall color options
- Automatic gain on frontent
- Multiple concurrent widebands
- Multiple concurrent rtl_tcp outputs
- Proper mobile browser support
- Add option to directly interface with software like SDR++

---

Expand Down
Binary file added readme/rtl-tcp-instance.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified readme/rtl-tcp-settings.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added readme/sdrpp-example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added readme/tuned-instance-example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified readme/waterfall-settings.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified readme/wideband-settings.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
35 changes: 20 additions & 15 deletions webapp/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@


current_wideband = None
current_server = None
current_server = {}

w_socket = None

Expand Down Expand Up @@ -58,15 +58,20 @@ def rtl_tcp_start():

rtl_tcp_data = request.json
tcp_settings = rtl_tcp_data.get("tcpSettings")
serverID = tcp_settings['serverID']

if current_server:
current_server.kill_all()
current_server = None
print("[*] Killed tcp server")
rtl_tcp_options = [tcp_settings['rtlServerIP'], tcp_settings['rtlServerPort'], tcp_settings['deviceID'], tcp_settings['rtlClientPort']]

print(current_server)

if serverID in current_server:
current_server[serverID].kill_all()
current_server.pop(serverID)
print("[*] Killed tcp server #" + serverID)

if tcp_settings['activeTCPServer'] is False:
current_server = Server(tcp_settings['rtlClientIP'], tcp_settings['rtlClientPort'])
current_server.async_server_start()
current_server[serverID] = Server(rtl_tcp_options)
current_server[serverID].async_server_start()

return jsonify({"status": "success"})

Expand All @@ -77,15 +82,15 @@ def rtl_tcp_start():
@app.route("/rtl_tcp_frequency", methods=["POST"])
def rtl_tcp_frequency():

if current_server:
frequency_data = request.json
set_frequency = frequency_data.get('currentTCPFreq')

print(frequency_data)

current_server.send_command(set_frequency)
global current_server

return jsonify({"status": "sucess"})
frequency_data = request.json
set_frequency = frequency_data.get('changeRtlFreq')
serverID = str(set_frequency['serverID'])

if serverID in current_server:
if current_server[serverID]:
current_server[serverID].send_command(set_frequency['frequency'])
return jsonify({"status": "sucess"})
else:
return jsonify({"status": "no current server!"})
68 changes: 51 additions & 17 deletions webapp/rtl_power_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

class Wideband:

def __init__(self, rtl_power_options):
def __init__(self, wideband_options):

# self.SET_FREQUENCY = 0x01

Expand All @@ -19,11 +19,20 @@ def __init__(self, rtl_power_options):
"dBm" : "0"
}

self.opts = rtl_power_options
self.wideband_options = wideband_options

self.freq_options = "{}M:{}M:{}".format(rtl_power_options['freqStart'], rtl_power_options['freqEnd'], rtl_power_options['fftBin'])
self.rtl_freq_options = "{}M:{}M:{}k".format(wideband_options['freqStart'], wideband_options['freqEnd'], wideband_options['fftBin'])
self.hackrf_freq_options = "{}:{}".format(wideband_options['freqStart'], wideband_options['freqEnd'])

self.power_args = ["rtl_power", "-f", self.freq_options, "-g", self.opts['gain'], "-i", "2", "-c", "0.20"]
self.rtl_power_args = ["rtl_power", "-f", self.rtl_freq_options, "-g", self.wideband_options['gain'], "-i", "2", "-c", "0.20"]
self.hackrf_sweep_args = ["hackrf_sweep", "-f", self.hackrf_freq_options, "-l", self.wideband_options['gain'], "-w", wideband_options['fftBin'] + "000"]

self.device_switch = {
"hackrf": self.hackrf_sweep_args,
"rtl-sdr": self.rtl_power_args
}

self.selected_device_args = self.device_switch[wideband_options['currentDevice']]

a = []

Expand Down Expand Up @@ -56,11 +65,11 @@ def stream_data(self):

"""This function contains all of the logic for selecting peaks from the rtl_power input."""

# print(self.power_args)
self.power_process = subprocess.Popen(self.power_args, stdout=subprocess.PIPE)
self.power_process = subprocess.Popen(self.selected_device_args, stdout=subprocess.PIPE)

low_freq = 0
full_scan = []
hackrf_scan = {}


while self.keep_running:
Expand All @@ -69,20 +78,45 @@ def stream_data(self):
break

if output:
wideband_out = output.decode().split(', ')
# Sets lowest frequency on the first pass
if self.wideband_options['currentDevice'] == 'rtl-sdr':
wideband_out = output.decode().split(', ')

# Sets lowest frequency on the first pass
if low_freq == 0:
low_freq = int(wideband_out[2])
full_scan += wideband_out[6:]

elif low_freq < int(wideband_out[2]):
full_scan += wideband_out[6:]

else:
socketio.emit('new_data', {'data': full_scan}, namespace='/')
full_scan = []
full_scan += wideband_out[6:]

elif self.wideband_options['currentDevice'] == 'hackrf':
wideband_out = output.decode().split(', ')
# Sets lowest frequency on the first pass

if low_freq == 0:
low_freq = int(wideband_out[2])
hackrf_scan[str(wideband_out[2])] = wideband_out[6:]

elif low_freq < int(wideband_out[2]):
hackrf_scan[str(wideband_out[2])] = wideband_out[6:]


if low_freq == 0:
low_freq = int(wideband_out[2])
full_scan += wideband_out[6:]
elif low_freq == int(wideband_out[2]):
sorted_freq_keys = sorted(hackrf_scan.keys())
full_scan = [item for key in sorted_freq_keys for item in hackrf_scan[key]]
socketio.emit('new_data', {'data': full_scan}, namespace='/')
full_scan = []
hackrf_scan = {}
hackrf_scan[str(wideband_out[2])] = wideband_out[6:]

elif low_freq < int(wideband_out[2]):
full_scan += wideband_out[6:]
else:
print("[!] Skipped scan integration")

else:
socketio.emit('new_data', {'data': full_scan}, namespace='/')
full_scan = []
full_scan += wideband_out[6:]

pass

Expand Down
20 changes: 12 additions & 8 deletions webapp/rtl_tcp_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,19 @@ def shutdown(self):

class Server:

def __init__(self, proxy_server_ip, proxy_server_port):
def __init__(self, options):

self.SET_FREQUENCY = 0x01

self.buffer_size = 4096
self.delay = 0.0001

self.client_ip = '127.0.0.1'
self.client_port = 1234
self.client_port = int(options[3])

self.server_ip = str(proxy_server_ip)
self.server_port = int(proxy_server_port)
self.server_ip = str(options[0])
self.server_port = int(options[1])
self.device_id = options[2]

self.input_list = []
self.channel = {}
Expand Down Expand Up @@ -124,7 +125,7 @@ def kill_all(self):

def _build_rtl_tcp(self):

self.tcp_process = subprocess.Popen(['rtl_tcp', '-d', '1', '-a', str(self.client_ip), '-p', str(self.client_port)], stdout=subprocess.PIPE)
self.tcp_process = subprocess.Popen(['rtl_tcp', '-d', self.device_id, '-g', '50', '-a', str(self.client_ip), '-p', str(self.client_port)], stdout=subprocess.PIPE)


def _on_accept(self):
Expand Down Expand Up @@ -170,9 +171,12 @@ def _on_recv(self):

def send_command(self, param):

cmd = struct.pack(">BI", self.SET_FREQUENCY, param)
print("[*] Sent")
self.forward.send(cmd)
try:
cmd = struct.pack(">BI", self.SET_FREQUENCY, param)
self.forward.send(cmd)
print("[*] Sent")
except Exception as e:
print("[!] send_command error :: ", e)


if __name__ == '__main__':
Expand Down
Loading

0 comments on commit 0163362

Please sign in to comment.