Skip to content

Commit

Permalink
Frequency Tracker: fixed REST API. New Python script to control frequ…
Browse files Browse the repository at this point in the history
…ency tracking via API
  • Loading branch information
f4exb committed May 5, 2019
1 parent ce12f40 commit 1a5f643
Show file tree
Hide file tree
Showing 275 changed files with 475 additions and 273 deletions.
13 changes: 13 additions & 0 deletions scriptsapi/Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
## Python scripts interfacing with the API ##

These scripts are designed to work in Python 3 preferably with version 3.6 or higher. Dependencies are installed with pip in a virtual environment. The sequence of operations is the following:

```
virtualenv -p /usr/bin/python3 venv # Create virtual environment
. ./venv/bin/activate # Activate virtual environment
pip install -r requirements.txt # Install requirements
```

<h2>freqtracking.py</h2>

This script is used to achieve frequency tracking with the FreqTracker plugin.
186 changes: 186 additions & 0 deletions scriptsapi/freqtracking.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
#!/usr/bin/env python3
'''
Frequency tracker:
Listens on a TCP port for SDRangel reverse API requests.
- When the request comes from a FreqTracker channel it gets the FreqTracker channel frequency shift
- When the request comes from another channel it records the difference between the FreqTracker frequency shift and
this channel frequency shift. Then it will periodically send a center frequency change request to this channel with
the FreqTracker channel frequency shift plus the difference thus achieving the locking of this channel to the FreqTracker
channel frequency.
- If the reply from the channel returns an error it will un-register the channel.
In the SDRangel instance you must activate the reverse API of the FreqTracker channel and the controlled channel(s)
reverse API giving the address and port of this instance of the script. You have to click on the small grey box at the
top left of the plugin GUI to open the channel details dialog where the reverse API can be configured.
'''

import requests
import time
import argparse
from flask import Flask
from flask import request, jsonify

SDRANGEL_API_ADDR = None
SDRANGEL_API_PORT = 8091
TRACKER_OFFSET = 0
TRACKER_DEVICE = 0
TRACKING_DICT = {}

app = Flask(__name__)

# ======================================================================
def getInputOptions():
""" This is the argument line parser """
# ----------------------------------------------------------------------
parser = argparse.ArgumentParser(description="Manages PTT from an SDRangel instance automatically")
parser.add_argument("-A", "--address", dest="addr", help="listening address (default 0.0.0.0)", metavar="IP", type=str)
parser.add_argument("-P", "--port", dest="port", help="listening port (default 8000)", metavar="PORT", type=int)
parser.add_argument("-a", "--address-sdr", dest="sdrangel_address", help="SDRangel REST API address (defaults to calling address)", metavar="ADDRESS", type=str)
parser.add_argument("-p", "--port-sdr", dest="sdrangel_port", help="SDRangel REST API port (default 8091)", metavar="PORT", type=int)

options = parser.parse_args()

if options.addr == None:
options.addr = "0.0.0.0"
if options.port == None:
options.port = 8000
if options.sdrangel_port == None:
options.sdrangel_port = 8091

return options.addr, options.port, options.sdrangel_address, options.sdrangel_port

# ======================================================================
def get_sdrangel_ip(request):
""" Extract originator address from request """
# ----------------------------------------------------------------------
if SDRANGEL_API_ADDR is not None:
return SDRANGEL_API_ADDR
if request.environ.get('HTTP_X_FORWARDED_FOR') is None:
return request.environ['REMOTE_ADDR']
else:
return request.environ['HTTP_X_FORWARDED_FOR']

# ======================================================================
def gen_dict_extract(key, var):
""" Gets a key value in a dictionnary or sub-dictionnary structure """
# ----------------------------------------------------------------------
if hasattr(var,'items'):
for k, v in var.items():
if k == key:
yield v
if isinstance(v, dict):
for result in gen_dict_extract(key, v):
yield result
elif isinstance(v, list):
for d in v:
for result in gen_dict_extract(key, d):
yield result

# ======================================================================
def update_frequency_setting(request_content, frequency):
""" Finds the channel settings key that contains the inputFrequencyOffset key
and replace it with a single inputFrequencyOffset key with new frequency
"""
# ----------------------------------------------------------------------
for k in request_content:
setting_item = request_content[k]
if isinstance(setting_item, dict):
if 'inputFrequencyOffset' in setting_item:
setting_item.update({
'inputFrequencyOffset': frequency
})


# ======================================================================
def adjust_channels(sdrangel_ip, sdrangel_port):
""" Adjust registered channels center frequencies
Remove keys for channels returning error
"""
# ----------------------------------------------------------------------
global TRACKING_DICT
base_url = f'http://{sdrangel_ip}:{sdrangel_port}/sdrangel'
remove_keys = []
for k in TRACKING_DICT:
device_index = k[0]
channel_index = k[1]
tracking_item = TRACKING_DICT[k]
frequency_correction = TRACKER_OFFSET - tracking_item['trackerFrequency']
frequency = tracking_item['channelFrequency'] + frequency_correction
update_frequency_setting(tracking_item['requestContent'], frequency)
r = requests.patch(url=base_url + f'/deviceset/{device_index}/channel/{channel_index}/settings', json=tracking_item['requestContent'])
if r.status_code / 100 != 2:
remove_keys.append(k)
for k in remove_keys:
TRACKING_DICT.pop(k, None)


# ======================================================================
def register_channel(device_index, channel_index, channel_frequency, request_content):
""" Register a channel or change its center frequency reference """
# ----------------------------------------------------------------------
global TRACKING_DICT
TRACKING_DICT.update({
(device_index, channel_index) : {
'channelFrequency': channel_frequency,
'trackerFrequency': TRACKER_OFFSET,
'requestContent': request_content
}
})

# ======================================================================
@app.route('/sdrangel')
def hello_sdrangel():
""" Just to test if it works """
# ----------------------------------------------------------------------
sdrangel_ip = get_sdrangel_ip(request)
print(f'SDRangel IP: {sdrangel_ip}')
return 'Hello, SDRangel!'


# ======================================================================
@app.route('/sdrangel/deviceset/<int:deviceset_index>/channel/<int:channel_index>/settings', methods=['GET', 'PATCH', 'PUT'])
def channel_settings(deviceset_index, channel_index):
""" Receiving channel settings from reverse API """
# ----------------------------------------------------------------------
global TRACKER_OFFSET
global TRACKER_DEVICE
orig_device_index = None
orig_channel_index = None
content = request.get_json(silent=True)
if content:
orig_device_index = content.get('originatorDeviceSetIndex')
orig_channel_index = content.get('originatorChannelIndex')
if orig_device_index is None or orig_channel_index is None:
print('device_settings: SDRangel reverse API v4.5.2 or higher required. No or invalid originator information')
return "SDRangel reverse API v4.5.2 or higher required "
sdrangel_ip = get_sdrangel_ip(request)
channel_type = content.get('channelType')
for freq_offset in gen_dict_extract('inputFrequencyOffset', content):
if channel_type == "FreqTracker":
print(f'SDRangel: {sdrangel_ip}:{SDRANGEL_API_PORT} Tracker: {freq_offset} Hz')
TRACKER_OFFSET = freq_offset
TRACKER_DEVICE = orig_device_index
adjust_channels(sdrangel_ip, SDRANGEL_API_PORT)
else:
register_channel(orig_device_index, orig_channel_index, freq_offset, content)
print(f'SDRangel: {sdrangel_ip}:{SDRANGEL_API_PORT} {channel_type} [{orig_device_index}:{orig_channel_index}] at {freq_offset} Hz')
return "OK processed "


# ======================================================================
def main():
""" This is the main routine """
# ----------------------------------------------------------------------
global SDRANGEL_API_ADDR
global SDRANGEL_API_PORT
addr, port, SDRANGEL_API_ADDR, SDRANGEL_API_PORT = getInputOptions()
print(f'main: starting at: {addr}:{port}')
app.run(debug=True, host=addr, port=port)


# ======================================================================
if __name__ == "__main__":
""" When called from command line... """
# ----------------------------------------------------------------------
main()
2 changes: 2 additions & 0 deletions scriptsapi/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
requests
Flask
1 change: 1 addition & 0 deletions sdrbase/resources/res.qrc
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
<file>webapi/doc/swagger/include/FileSource.yaml</file>
<file>webapi/doc/swagger/include/FreeDVDemod.yaml</file>
<file>webapi/doc/swagger/include/FreeDVMod.yaml</file>
<file>webapi/doc/swagger/include/FreqTracker.yaml</file>
<file>webapi/doc/swagger/include/HackRF.yaml</file>
<file>webapi/doc/swagger/include/LimeSdr.yaml</file>
<file>webapi/doc/swagger/include/LocalInput.yaml</file>
Expand Down
4 changes: 2 additions & 2 deletions sdrbase/resources/webapi/doc/html2/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5950,7 +5950,7 @@ <h1>SDRangel</h1>
<div id="header">
<div id="api-_">
<h2 id="welcome-to-apidoc">API and SDK Documentation</h2>
<div class="app-desc">Version: 4.6.0</div>
<div class="app-desc">Version: 4.7.0</div>
<hr>
<div><p>This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube</p>
<hr />
Expand Down Expand Up @@ -24924,7 +24924,7 @@ <h3> Status: 501 - Function not implemented </h3>
</div>
<div id="generator">
<div class="content">
Generated 2019-05-05T11:13:49.637+02:00
Generated 2019-05-05T23:39:03.203+02:00
</div>
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion sdrbase/resources/webapi/doc/swagger/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ info:
---
version: "4.6.0"
version: "4.7.0"
title: SDRangel
contact:
url: "https://github.com/f4exb/sdrangel"
Expand Down
2 changes: 1 addition & 1 deletion swagger/sdrangel/api/swagger/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ info:
---
version: "4.6.0"
version: "4.7.0"
title: SDRangel
contact:
url: "https://github.com/f4exb/sdrangel"
Expand Down
4 changes: 2 additions & 2 deletions swagger/sdrangel/code/html2/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5950,7 +5950,7 @@ <h1>SDRangel</h1>
<div id="header">
<div id="api-_">
<h2 id="welcome-to-apidoc">API and SDK Documentation</h2>
<div class="app-desc">Version: 4.6.0</div>
<div class="app-desc">Version: 4.7.0</div>
<hr>
<div><p>This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube</p>
<hr />
Expand Down Expand Up @@ -24924,7 +24924,7 @@ <h3> Status: 501 - Function not implemented </h3>
</div>
<div id="generator">
<div class="content">
Generated 2019-05-05T11:13:49.637+02:00
Generated 2019-05-05T23:39:03.203+02:00
</div>
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion swagger/sdrangel/code/qt5/client/SWGAMDemodReport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* SDRangel
* This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time ---
*
* OpenAPI spec version: 4.6.0
* OpenAPI spec version: 4.7.0
* Contact: [email protected]
*
* NOTE: This class is auto generated by the swagger code generator program.
Expand Down
2 changes: 1 addition & 1 deletion swagger/sdrangel/code/qt5/client/SWGAMDemodReport.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* SDRangel
* This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time ---
*
* OpenAPI spec version: 4.6.0
* OpenAPI spec version: 4.7.0
* Contact: [email protected]
*
* NOTE: This class is auto generated by the swagger code generator program.
Expand Down
2 changes: 1 addition & 1 deletion swagger/sdrangel/code/qt5/client/SWGAMDemodSettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* SDRangel
* This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time ---
*
* OpenAPI spec version: 4.6.0
* OpenAPI spec version: 4.7.0
* Contact: [email protected]
*
* NOTE: This class is auto generated by the swagger code generator program.
Expand Down
2 changes: 1 addition & 1 deletion swagger/sdrangel/code/qt5/client/SWGAMDemodSettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* SDRangel
* This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time ---
*
* OpenAPI spec version: 4.6.0
* OpenAPI spec version: 4.7.0
* Contact: [email protected]
*
* NOTE: This class is auto generated by the swagger code generator program.
Expand Down
2 changes: 1 addition & 1 deletion swagger/sdrangel/code/qt5/client/SWGAMModReport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* SDRangel
* This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time ---
*
* OpenAPI spec version: 4.6.0
* OpenAPI spec version: 4.7.0
* Contact: [email protected]
*
* NOTE: This class is auto generated by the swagger code generator program.
Expand Down
Loading

0 comments on commit 1a5f643

Please sign in to comment.