API
The CloudRF API offers a powerful and scalable service to model almost any radio hardware, anywhere. When integrated into another system you can automate modelling to save significant time and money and realise capabilities not possible with a modest budget. The next generation performance also unlocks new analysis possibilities allowing “real time” decision making for autonomous vehicles and drones, for example.
Use cases
Automate customer qualification based upon a zip code
Robot / drone route selection
Best site analysis for optimal & economic deployments
Receiver modelling for signal multi-lateration
Generation of network coverage maps for marketing or briefing rooms
Automated regression testing for network changes
Integration with cognitive radios for smarter site/frequency/power selection based on topography
OpenAPI 3 Schema
For a complete OpenAPI 3 schema of the CloudRF API please consult the Swagger UI documentation.
API Endpoint
For CloudRF users, the API endpoint is https://api.cloudrf.com
.
Users with a SOOTHSAYER server will have an IP address instead.
Security
All requests are encrypted at the transport layer with TLS.
Authentication
Each user has a unique private API key which resembles a long random string of characters. You should protect this key to prevent unauthorised use of your account.
An example of such API key is as below:
101-ec94622a4cb939a77101a118c6871d03cea88af3
API Key Security
It is important to keep your API key secure. Publicly exposing your key can compromise your account, which could result in a loss of data or unexpected charges. A key does not give access to your interface or shop login which is separate.
To keep your API keys secure, follow some best practices:
Keys as Environment Variables
Store API keys as environment variables. This has an added benefit of accessing your key via a friendly environment variable name rather than having to remember your full key each time.
Do not embed API keys directly in code. API keys that are embedded in code can be accidentally exposed outside your circle of trust.
Keys as Files
You can store your API key in a file and make reference to that file each time you need to use your API key.
If you store API keys in files, store those files outside your application’s source tree.
Doing so helps to ensure that your keys do not end up in your source code version control system. This is particularly important if you use a public source code management system such as GitHub.
Using the API Key
The CloudRF API requires API key authentication in the request header. This provides a key-value pair with key
as the first value and your personal key as the second, paired value.
For example, Postman allows you to enter your key from their interface.
API “Hello World!” Getting Started Example
Send this curl request to model coverage for a VHF radio, 2m above the water with 1W of power:
curl --location 'https://api.cloudrf.com/area' \
--header 'key: YOUR-API-KEY-GOES-HERE' \
--data '{
"site": "HelloWorld",
"network": "Testing",
"transmitter": {
"lat": 38.916,
"lon": 1.448,
"alt": 2,
"frq": 160,
"txw": 1,
"bwi": 1
},
"receiver": {
"lat": 0,
"lon": 0,
"alt": 2,
"rxg": 0,
"rxs": -90
},
"antenna": {
"txg": 0,
"txl": 0,
"ant": 1,
"azi": 0
},
"output": {
"units": "metric",
"col": "LTE.dBm",
"out": 2,
"res": 30,
"rad": 5
}
}'
Example API Requests
Below lists some basic examples of some of the most common types of requests made to the CloudRF API.
For a complete list of available schemas please consult the Swagger UI documentation.
With all requests some values are required and so the response will return any validation errors or any failures should your request not be able to be processed.
Area
The area
endpoint accepts a JSON object in the request body describing your network and will run a point-to-multipoint “heatmap” calculation.
Request
The below example is for an omni-directional antenna on an 8m mast at 446MHz. The request is sent as a POST
request to https://api.cloudrf.com/area
and it will return a JSON response containing metadata and URLs to image layers for your map.
{
"site": "Harbour",
"network": "PMR",
"engine": "2",
"transmitter": {
"lat": "38.913767",
"lon": "1.440017",
"alt": "8",
"frq": "446",
"txw": "1",
"bwi": "0.1"
},
"receiver": {
"lat": 0,
"lon": 0,
"alt": "2",
"rxg": "2",
"rxs": "-90"
},
"antenna": {
"txg": "2.15",
"txl": "0",
"ant": "1",
"azi": "0",
"tlt": "0",
"hbw": "0",
"vbw": "0",
"fbr": "0",
"pol": "v"
},
"model": {
"pm": "1",
"pe": "2",
"ked": "1",
"rel": "50"
},
"environment": {
"clt": "Minimal.clt",
"elevation": "2",
"landcover": "1",
"buildings": "0",
"obstacles": "0"
},
"output": {
"units": "m",
"col": "RAINBOW45.dBm",
"out": "2",
"nf": "-124",
"res": "20",
"rad": "8"
}
}
Response
The PNG_Mercator
image is warped for slippy maps like Mapbox, Leaflet and Google Maps. The PNG_WGS84
is for globes like Google Earth, Cesium and WinTAK.
{
"kmz": "https://api.cloudrf.com/archive/eFYyWFpUYW0zR1pxcTIyRlRKREQxUT09/kmz",
"PNG_Mercator": "https://api.cloudrf.com/users/1/0812210016_PMR_harbour.3857.png",
"PNG_WGS84": "https://api.cloudrf.com/users/1/0812210016_PMR_harbour.4326.png",
"bounds": [
39.00613,
1.532378,
38.82141,
1.347656
],
"id": 8191195,
"sid": "eFYyWFpUYW0zR1pxcTIyRlRKREQxUT09",
"area": 55.9,
"coverage": 28.0,
"key": [
{
"l": "-45dBm",
"r": 37,
"g": 131,
"b": 255
},
{
"l": "-55dBm",
"r": 46,
"g": 254,
"b": 187
},
{
"l": "-65dBm",
"r": 98,
"g": 254,
"b": 55
},
{
"l": "-75dBm",
"r": 254,
"g": 234,
"b": 63
},
{
"l": "-85dBm",
"r": 254,
"g": 72,
"b": 72
}
],
"elapsed": 2297.0,
"balance": 23476
}
PNG image with RGB colours which map to the signal levels in the JSON output:
Go Faster With The GPU Engine
Switch the engine parameter to 1
to use the GPU engine. All other settings are the same.
{
"site": "Harbour",
"network": "PMR",
"engine": "1",
... snipped for brevity ...
}
Please note that to be able to make use of the GPU engine you require an active GPU subscription or SOOTHSAYER with GPU functionality enabled.
Multi-Azimuth Requests
You can specify an array of azimuths to model panels on a cell tower for custom patterns and templates. Pass azimuths in as a comma separated list in quotes like "0,90,180,270"
. The maximum number of azimuths you can pass in one API request is 90. This works for CPU and GPU engines.
{
"site": "FourPanels",
"network": "800MHZ",
"engine": "2",
"transmitter": {
"lat": "38.881037",
"lon": "1.468",
"alt": "12",
"frq": "800",
"txw": "1",
"bwi": "0.1"
},
"receiver": {
"lat": 0,
"lon": 0,
"alt": "2",
"rxg": "2",
"rxs": "-90"
},
"antenna": {
"txg": "21",
"txl": "0",
"ant": 0,
"azi": "0,90,180,270",
"tlt": "1",
"hbw": "80",
"vbw": "80",
"fbr": "21",
"pol": "v"
},
"model": {
"pm": "11",
"pe": "2",
"ked": "1",
"rel": "50"
},
"environment": {
"clt": "Minimal.clt",
"elevation": "2",
"landcover": "0",
"buildings": "0",
"obstacles": "0"
},
"output": {
"units": "m",
"col": "RAINBOW45.dBm",
"out": "2",
"nf": "-120",
"res": "20",
"rad": "5"
}
}
Path
The path
endpoint accepts a JSON object describing your network and will run a point-to-point calculation.
Request
The following example includes some optional parameters and provides the required data to model a link with land cover and 3D buildings enabled at 30m resolution. The request is similar to the area
call except the receiver latitude (lat
) and longitude (lon
) values are populated.
The below example is sent as a POST
request to https://api.cloudrf.com/path
.
{
"site": "HarbourLink",
"network": "LPWAN",
"transmitter": {
"lat": "38.9090",
"lon": "1.44094",
"alt": 12,
"frq": "868",
"txw": "0.1",
"bwi": "0.1"
},
"receiver": {
"lat": "38.9173881",
"lon": "1.46864923",
"alt": 12,
"rxg": "2",
"rxs": "-90"
},
"antenna": {
"txg": "2.15",
"txl": "0",
"ant": "1",
"azi": "0",
"tlt": "0",
"hbw": "1",
"vbw": "1",
"pol": "v"
},
"model": {
"pm": "1",
"pe": "2",
"ked": "1",
"rel": "95"
},
"environment": {
"clt": "Minimal.clt",
"elevation": "2",
"landcover": "1",
"buildings": "1",
"obstacles": "0"
},
"output": {
"units": "m",
"col": "RAINBOW45.dBm",
"out": "2",
"res": "30",
"rad": "2"
}
}
Response
The response contains metadata about the link along with raw values necessary to build a chart using a graphing library such as Matplotlib or Plotly. It also contains a link to a KMZ so you can see the profile in 3D.
{
"Engine": "Sleipnir 1.7.8",
"Frequency MHz": 868,
"Propagation model": "ITM",
"Earth dielectric constant": 13,
"Earth conductivity": 0.002,
"Radio climate": "Maritime Temperate (Land)",
"Atmospheric bending constant": 301,
"Fraction of situations": 95,
"Fraction of time": 95,
"Receiver": [
{
"Latitude": 38.91739,
"Longitude": 1.468649,
"Ground elevation m": 28,
"Antenna height m": 12,
"Receiver gain dBd": -0.15,
"Receiver gain dBi": 2
}
],
"Transmitters": [
{
"Latitude": 38.90908,
"Longitude": 1.440943,
"Ground elevation m": 1,
"Antenna height m": 12,
"Distance to receiver km": 2.572,
"Azimuth to receiver deg": 68.92,
"Downtilt angle deg": 0.6,
"Antenna gain dBd": 0,
"Antenna gain dBi": 2.15,
"Polarisation": "Vertical",
"Power W": 0.1,
"Power dBm": 22.15,
"ERP W": 0.1,
"EIRP W": 0.164,
"ERP dBm": 20,
"EIRP dBm": 22.15,
"Free space path loss dB": 95.3,
"Bandwidth MHz": 0.1,
"Johnson Nyquist noise dB": 0.2,
"Noise floor dBm": -120,
"Channel noise dBm": -119.8,
"Signal power at receiver dBm": -88.6,
"Signal to Noise Ratio dB": 31.2,
"Computed path loss dB": 108.6,
"Model attenuation dB": 13.3,
"Field strength at receiver dBuV/m": 49.6,
"RX voltage 50 ohm dipole uV": 14,
"RX voltage 50 ohm dipole dBuV": 23,
"RX voltage 75 ohm dipole uV": 17,
"RX voltage 75 ohm dipole dBuV": 24,
"Raise RX antenna for LOS": 0,
"Raise RX antenna for fresnel 60%": 0,
"Raise RX antenna for full fresnel": 19,
"Obstructions": [],
"Distance": [
0.03,
0.059,
0.089,
0.119,
0.148,
... snipped for brevity ...
],
"Terrain": [
1,
1,
1,
1,
1,
... snipped for brevity ...
],
"Terrain_AMSL": [
1,
0,
0,
0,
0,
... snipped for brevity ...
],
"Landcover distance": [
0.03,
0.059,
0.089,
0.119,
0.148,
... snipped for brevity ...
],
"Landcover codes": [
80,
80,
80,
80,
80,
... snipped for brevity ...
],
"Landcover heights": [
1,
1,
1,
1,
1,
... snipped for brevity ...
],
"Fresnel": [
0,
-3.18,
-4.47,
-5.45,
-6.25,
... snipped for brevity ...
],
"dBm": [
-50,
-56,
-60,
-62,
-64,
... snipped for brevity ...
],
"dB": [
70,
76,
80,
82,
84,
... snipped for brevity ...
]
}
],
"calculation_adjusted": [],
"elapsed": 130.0,
"Chart image": "https://api.cloudrf.com/API/archive/data?sid=c2ZrYXdUa0tPeGFrbG5wS1dLd3RIdz09&type=ppa",
"kmz": "https://api.cloudrf.com/API/archive/data?sid=c2ZrYXdUa0tPeGFrbG5wS1dLd3RIdz09&type=path"
}
Points
The points
endpoint accepts a JSON object describing an array of points (transmitters) which will be tested back to a single point (receiver).
Request
The following example is for route a boat will take. The location varies with each point but all the other values are constant.
The request is sent as a POST
body to https://api.cloudrf.com/points
.
{
"site": "RIB",
"network": "VHF",
"transmitter": {
"lat": 38.914381,
"lon": 1.436988,
"alt": "2",
"frq": "160",
"txw": "1",
"bwi": "0.1"
},
"points": [
{
"lat": 38.91086406303705,
"lon": 1.444486542453175,
"alt": 2
},
{
"lat": 38.91041372754393,
"lon": 1.444495874752767,
"alt": 2
},
{
"lat": 38.90996339201471,
"lon": 1.4445052069344337,
"alt": 2
},
{
"lat": 38.909513056449505,
"lon": 1.444514538998178,
"alt": 2
}
],
"receiver": {
"lat": 38.914381,
"lon": 1.436988,
"alt": 2,
"rxg": "2",
"rxs": "-90"
},
"antenna": {
"txg": "2.15",
"txl": "0",
"ant": "1",
"azi": "0",
"tlt": "0",
"hbw": "1",
"vbw": "1",
"fbr": "2.15",
"pol": "v"
},
"model": {
"pm": "11",
"pe": "2",
"ked": "1",
"rel": "50"
},
"environment": {
"elevation": "2",
"landcover": "1",
"buildings": "0",
"obstacles": "0"
},
"output": {
"units": "m",
"col": "RAINBOW45.dBm",
"out": "2",
"nf": "-124",
"res": "30",
"rad": "8"
}
}
Response
The response contains JSON metadata for the points, in this case received power levels for each point.
{
"Engine": "Sleipnir 1.7.8",
"Frequency MHz": 160,
"Propagation model": "Egli VHF/UHF",
"Model subtype": "Suburban",
"Receiver": [
{
"Latitude": 38.91438,
"Longitude": 1.436988,
"Ground elevation m": 1,
"Antenna height m": 2,
"Receiver gain dBd": -0.15,
"Receiver gain dBi": 2
}
],
"Transmitters": [
{
"Latitude": 38.91086,
"Longitude": 1.444487,
"Ground elevation m": 0,
"Antenna height m": 2,
"Distance to receiver km": 0.758,
"Azimuth to receiver deg": 301.08,
"Downtilt angle deg": 0.1,
"Antenna gain dBd": 0,
"Antenna gain dBi": 2.15,
"Polarisation": "Vertical",
"Power W": 1,
"Power dBm": 2.15,
"ERP W": 0.001,
"EIRP W": 1.641,
"ERP dBm": 0,
"EIRP dBm": 2.15,
"Free space path loss dB": 70,
"Bandwidth MHz": 0.1,
"Johnson Nyquist noise dB": 0.2,
"Noise floor dBm": -124,
"Channel noise dBm": -123.8,
"Signal power at receiver dBm": -66.2,
"Signal to Noise Ratio dB": 57.6,
"Computed path loss dB": 96.2,
"Model attenuation dB": 26.2,
"Field strength at receiver dBuV/m": 27.3,
"Raise RX antenna for LOS": 5,
"Raise RX antenna for fresnel 60%": 77,
"Raise RX antenna for full fresnel": 128,
"server": 1
},
{
"Latitude": 38.91041,
"Longitude": 1.444496,
"Ground elevation m": 0,
"Antenna height m": 2,
"Distance to receiver km": 0.786,
"Azimuth to receiver deg": 304.18,
"Downtilt angle deg": 0.1,
"Antenna gain dBd": 0,
"Antenna gain dBi": 2.15,
"Polarisation": "Vertical",
"Power W": 1,
"Power dBm": 2.15,
"ERP W": 0.001,
"EIRP W": 1.641,
"ERP dBm": 0,
"EIRP dBm": 2.15,
"Free space path loss dB": 70.3,
"Bandwidth MHz": 0.1,
"Johnson Nyquist noise dB": 0.2,
"Noise floor dBm": -124,
"Channel noise dBm": -123.8,
"Signal power at receiver dBm": -66.8,
"Signal to Noise Ratio dB": 57,
"Computed path loss dB": 96.8,
"Model attenuation dB": 26.5,
"Field strength at receiver dBuV/m": 26.7,
"Raise RX antenna for LOS": 5,
"Raise RX antenna for fresnel 60%": 80,
"Raise RX antenna for full fresnel": 132,
"server": 2
},
{
"Latitude": 38.90996,
"Longitude": 1.444505,
"Ground elevation m": 0,
"Antenna height m": 2,
"Distance to receiver km": 0.816,
"Azimuth to receiver deg": 307.06,
"Downtilt angle deg": 0.1,
"Antenna gain dBd": 0,
"Antenna gain dBi": 2.15,
"Polarisation": "Vertical",
"Power W": 1,
"Power dBm": 2.15,
"ERP W": 0.001,
"EIRP W": 1.641,
"ERP dBm": 0,
"EIRP dBm": 2.15,
"Free space path loss dB": 70.6,
"Bandwidth MHz": 0.1,
"Johnson Nyquist noise dB": 0.2,
"Noise floor dBm": -124,
"Channel noise dBm": -123.8,
"Signal power at receiver dBm": -67.5,
"Signal to Noise Ratio dB": 56.3,
"Computed path loss dB": 97.5,
"Model attenuation dB": 26.9,
"Field strength at receiver dBuV/m": 26,
"Raise RX antenna for LOS": 4,
"Raise RX antenna for fresnel 60%": 82,
"Raise RX antenna for full fresnel": 136,
"server": 3
},
{
"Latitude": 38.90951,
"Longitude": 1.444515,
"Ground elevation m": 0,
"Antenna height m": 2,
"Distance to receiver km": 0.848,
"Azimuth to receiver deg": 309.74,
"Downtilt angle deg": 0.1,
"Antenna gain dBd": 0,
"Antenna gain dBi": 2.15,
"Polarisation": "Vertical",
"Power W": 1,
"Power dBm": 2.15,
"ERP W": 0.001,
"EIRP W": 1.641,
"ERP dBm": 0,
"EIRP dBm": 2.15,
"Free space path loss dB": 70.9,
"Bandwidth MHz": 0.1,
"Johnson Nyquist noise dB": 0.2,
"Noise floor dBm": -124,
"Channel noise dBm": -123.8,
"Signal power at receiver dBm": -68.1,
"Signal to Noise Ratio dB": 55.7,
"Computed path loss dB": 98.1,
"Model attenuation dB": 27.2,
"Field strength at receiver dBuV/m": 25.3,
"Raise RX antenna for LOS": 4,
"Raise RX antenna for fresnel 60%": 85,
"Raise RX antenna for full fresnel": 141,
"server": 4
}
],
"calculation_adjusted": [],
"elapsed": 111.0,
"kmz": "https://api.cloudrf.com/API/archive/data?points=0812220045_mymesh_PPA_POINTS&uid=1",
"json": "https://api.cloudrf.com/users/1/0812220045_mymesh_PPA_POINTS.json"
}
Best Site Analysis
The bsa
endpoint accepts a JSON object in the request body describing a location with a radius, similar to an ‘area’ call except it uses a random Monte-Carlo technique to rank locations within the area to identify the best. The colour key is irrelevant for the API since the response is always a greyscale image which you must style.
Request
The below example is for a planned Sub GHz LPWAN gateway in the hills. The request is sent as a POST
request to https://api.cloudrf.com/bsa
and it will return a JSON response containing metadata and URLs to image layers for your map.
{
"site": "BestSite",
"network": "Ibiza",
"engine": "1",
"transmitter": {
"lat": 38.938092,
"lon": 1.389430,
"alt": "2",
"frq": "446",
"txw": "1",
"bwi": "0.1"
},
"receiver": {
"lat": 0,
"lon": 0,
"alt": "2",
"rxg": "2",
"rxs": "-90"
},
"antenna": {
"txg": "2.15",
"txl": "0",
"ant": "1",
"azi": "0",
"tlt": "0",
"hbw": "0",
"vbw": "0",
"fbr": "2.15",
"pol": "v"
},
"model": {
"pm": "7",
"pe": "2",
"ked": "0",
"rel": "95"
},
"environment": {
"clt": "Minimal.clt",
"elevation": "2",
"landcover": "0",
"buildings": "0",
"obstacles": "0"
},
"output": {
"units": "m",
"col": "BSA8.bsa",
"out": "7",
"nf": "-120",
"res": "10",
"rad": 3
}
}
Response
The PNG_Mercator
image is warped for slippy maps like Mapbox, Leaflet and Google Maps. The PNG_WGS84
is for globes like Google Earth, Cesium and WinTAK.
A BSA response is almost identical to an area response. The real difference is in the image which is always greyscale where white is 100% efficiency and black is 0% efficiency. The colour key returned can be used for styling by isolating only the red channel since the green and blue channels will be the same eg. RGB(254,254,254) = 95% coverage. The area and coverage fields are zero since this is not a site.
{
"kmz": "https://api.cloudrf.com/archive/a2s1bWJkTDJYdGMrNUNHZVhpTUtUQT09/kmz",
"PNG_Mercator": "https://api.cloudrf.com/output/projection/3857/1/0816125807_IBIZA_BestSite",
"PNG_WGS84": "https://api.cloudrf.com/users/1/0816125807_IBIZA_BestSite.4326.png",
"bounds": [
38.96563941396777,
1.4248893862148355,
38.91054585887422,
1.3539723152977645
],
"id": 8201477,
"sid": "a2s1bWJkTDJYdGMrNUNHZVhpTUtUQT09",
"area": 0,
"coverage": 0,
"key": [
{
"l": "95%",
"r": 254,
"g": 0,
"b": 0
},
{
"l": "90%",
"r": 254,
"g": 91,
"b": 0
},
{
"l": "85%",
"r": 254,
"g": 183,
"b": 0
},
{
"l": "80%",
"r": 236,
"g": 254,
"b": 0
},
{
"l": "75%",
"r": 145,
"g": 254,
"b": 0
},
{
"l": "70%",
"r": 53,
"g": 254,
"b": 0
},
{
"l": "65%",
"r": 0,
"g": 254,
"b": 38
},
{
"l": "60%",
"r": 0,
"g": 254,
"b": 129
},
{
"l": "55%",
"r": 0,
"g": 254,
"b": 221
},
{
"l": "50%",
"r": 0,
"g": 198,
"b": 255
}
],
"elapsed": 1147.0,
"balance": 23305
}
A BSA response is shown below. Notice that ridgelines rank high, but not as high as the low ground to the east which has the best visibility of the entire area:
Request With A Polygon
If you want to use a polygon shape to define boundaries, you can use the edges
array to define each point. A minimum of 3 points are needed as lat
/lon
pairs within the edges
array. With this you can draw political or property boundaries. A radius is automatically computed using this method so it doesn’t matter what radius you request.
{
"edges": [
{
"lat": 38.94,
"lon": 1.39
},
{
"lat": 38.94,
"lon": 1.399
},
{
"lat": 38.945,
"lon": 1.395
}
],
"site": "Triangle",
"network": "Ibiza",
"engine": "1",
"transmitter": {
"lat": 38.945,
"lon": 1.392,
"alt": "2",
"frq": "446",
"txw": "1",
"bwi": "0.1"
},
... snipped for brevity ...
}
By defining 3 points, we have reduced the study area to a triangle:
Multisite
The multisite
endpoint is a GPU only function for simulating many transmitters at once. It accepts a JSON object in the request body describing an array of transmitters
, using familiar fields from an area
call. An antenna is defined for every transmitter so you can have different patterns in a network. You can even model a group of distant radios with omni antennas and a long range parabolic in the same request.
It renders the legacy mesh
API call obsolete (unless you only have a CPU) as it merges sites by design.
Transmitters must be within 2000km of each other. eg. You cannot request a multisite across the Ocean.
Antenna azimuths must be between 0 and 359 degrees.
Request
The below example is for three local UHF radios, with omni dipoles (ant: 1) in the hills. The request is sent as a POST
request to https://api.cloudrf.com/multisite
and it will return a JSON response containing metadata and URLs to image layers for your map.
{
"site": "3amigos",
"network": "UHF",
"transmitters": [
{
"lat": 38.941501808741165,
"lon": 1.3709467181497763,
"alt": 2,
"frq": 868,
"txw": 1,
"bwi": 0.1,
"ant": 0,
"antenna": {
"txg": 2.15,
"txl": 0,
"ant": 1,
"azi": 0,
"tlt": 0,
"hbw": 1,
"vbw": 1,
"fbr": 2.15,
"pol": "v"
}
},
{
"lat": 38.94210625018613,
"lon": 1.3847431304250852,
"alt": 2,
"frq": 868,
"txw": 1,
"bwi": 0.1,
"ant": 0,
"antenna": {
"txg": 2.15,
"txl": 0,
"ant": 1,
"azi": 0,
"tlt": 0,
"hbw": 1,
"vbw": 1,
"fbr": 2.15,
"pol": "v"
}
},
{
"lat": 38.94370157813472,
"lon": 1.4006969012717958,
"alt": 2,
"frq": 868,
"txw": 1,
"bwi": 0.1,
"ant": 0,
"antenna": {
"txg": 2.15,
"txl": 0,
"ant": 1,
"azi": 0,
"tlt": 0,
"hbw": 1,
"vbw": 1,
"fbr": 2.15,
"pol": "v"
}
}
],
"receiver": {
"alt": 2,
"rxg": 2,
"rxs": -105
},
"model": {
"pm": 11,
"pe": 2,
"ked": 1,
"rel": 95
},
"environment": {
"clm": 0,
"cll": 1,
"clt": "Minimal.clt"
},
"output": {
"units": "m",
"col": "LTE.dBm",
"out": 2,
"nf": -120,
"res": 10,
"rad": 2
}
}
Response
The PNG_Mercator
image is warped for slippy maps like Mapbox, Leaflet and Google Maps. The PNG_WGS84
is for globes like Google Earth, Cesium and WinTAK.
{
"kmz": "https://api.cloudrf.com/archive/WUMya3l4a3ZmcVFDajZ4THZVckVuQT09/kmz",
"PNG_Mercator": "https://api.cloudrf.com/output/projection/3857/1/0816124833_UAS_PPA_MULTISITE",
"PNG_WGS84": "https://api.cloudrf.com/users/1/0816124833_UAS_PPA_MULTISITE.4326.png",
"bounds": [
38.96165650015791,
1.4238207375342375,
38.92354146204287,
1.3478216615351617
],
"id": 8201557,
"sid": "WUMya3l4a3ZmcVFDajZ4THZVckVuQT09",
"area": 43,
"coverage": 0,
"key": [
{
"l": "-60dBm",
"r": 248,
"g": 14,
"b": 14
},
{
"l": "-65dBm",
"r": 248,
"g": 107,
"b": 14
},
{
"l": "-70dBm",
"r": 247,
"g": 199,
"b": 14
},
{
"l": "-75dBm",
"r": 203,
"g": 247,
"b": 14
},
{
"l": "-80dBm",
"r": 110,
"g": 247,
"b": 14
},
{
"l": "-85dBm",
"r": 17,
"g": 246,
"b": 13
},
{
"l": "-90dBm",
"r": 13,
"g": 246,
"b": 102
},
{
"l": "-95dBm",
"r": 13,
"g": 246,
"b": 194
},
{
"l": "-100dBm",
"r": 13,
"g": 205,
"b": 245
},
{
"l": "-105dBm",
"r": 13,
"g": 113,
"b": 245
}
],
"elapsed": 430.0,
"balance": 23248
}
RADAR With Mixed Heights Example
The below example shows how to use the multisite
API with RADAR transmitters (model.pm
value of 8
) where the transmitters are positioned with an elevation relative to the ground (AGL), and the receiver has an elevation above sea level (AMSL). This is indicated with an output.tx_units
value of m
and output.rx_units
value of m_amsl
, respectively.
{
"site": "Multisite",
"network": "RADAR_TEST",
"transmitters": [
{
"lat": 30.510523,
"lon": -86.830194,
"alt": 5,
"frq": 8000,
"txw": 100,
"bwi": 1.2,
"antenna": {
"txg": 15,
"txl": 0,
"ant": 1,
"azi": 0,
"tlt": 0,
"hbw": 30,
"vbw": 30,
"fbr": 0,
"pol": "v"
}
},
{
"lat": 30.500163,
"lon": -86.814786,
"alt": 5,
"frq": 8000,
"txw": 100,
"bwi": 1.2,
"antenna": {
"txg": 15,
"txl": 0,
"ant": 1,
"azi": 0,
"tlt": 0,
"hbw": 30,
"vbw": 30,
"fbr": 0,
"pol": "v"
}
}
],
"receiver": {
"alt": 500,
"rxg": 0,
"rxs": -100
},
"model": {
"pm": 8,
"pe": 2,
"ked": 1,
"rel": 50,
"rcs": 100
},
"environment": {
"elevation": 2,
"cll": 0,
"clm": 0,
"clt": "Minimal.clt"
},
"output": {
"tx_units": "m",
"rx_units": "m_amsl",
"col": "RAINBOW.dBm",
"out": 2,
"nf": -120,
"res": 50,
"rad": 100
}
}
Mesh
The mesh
function is a legacy function which merges pre-calcuated layers. It was made obsolete by the multisite
function but is still useful for post-processing such as creating network coverage maps.
Request
The below example is a HTTP GET
request for meshing the layers (max 1000) belonging to the BLUENET
network and using the name mymap
for the output. You must send your API key in the request header as normal.
https://api.cloudrf.com/mesh?network=BLUENET&name=mymap
You can also specify up to 100 sites in the URL as a list:
https://api.cloudrf.com/mesh?network=mymap&calcs=0816143545_BLUENET_Site,0816143541_BLUENET_Site
Response
The response is a JSON object containing images and metadata about the calculations which were used to create the mesh image:
{
"png_wgs84": "https://api.cloudrf.com/users/1/0816133557_Mesh.4326.png",
"png_mercator": "https://api.cloudrf.com/users/1/0816133557_Mesh.3857.png",
"kmz": "https://api.cloudrf.com/archive/WkYveUVDdmZTNG1IbDdPbjVKUVVLUT09/kmz",
"id": 8201614,
"sid": "WkYveUVDdmZTNG1IbDdPbjVKUVVLUT09",
"filename": "0816133557_Mesh",
"bounds": [
38.96722,
1.4228695899280575,
38.919690503597124,
1.363707
],
"calculations": [
8201611,
8201612
]
}
Interference
The interference
function is a legacy function which merges pre-calcuated layers like mesh
to reveal the strongest signal where coverage overlaps. It makes decisions for each touched pixel and promotes the strongest signal.
At the time of writing (December 2023) this is currently a CPU-only functionality, but there are plans to enhance this functionality with GPU capabilities.
Request
The below example is a HTTP GET
request for meshing the layers (max 1000) belonging to the BLUENET network and using the name QRM
for the output. You must send your API key in the request header as normal.
https://api.cloudrf.com/interference/?network=BLUENET&name=QRM
Response
The response is a JSON object containing images and metadata about the calculations which were used to create the interference image:
{
"png_wgs84": "https://api.cloudrf.com/users/1/0816134222_QRM.4326.png",
"png_mercator": "https://api.cloudrf.com/users/1/0816134222_QRM.3857.png",
"id": 8201623,
"sid": "a0FTWEVXOGp2QzRyMjlGOGVhVUxzQT09",
"filename": "0816134222_QRM",
"bounds": [
38.96722,
1.42287,
38.919691,
1.363707
],
"calculations": [
8201611,
8201612
]
}
Network
The network
function is a legacy function which tests links to pre-calcuated sites to reveal the strongest server(s) at the tested receiver site (eg. a customer’s house).
Request
The below example is a HTTP GET
request for testing a location with receiver gain 2dBi and receiver height 2m to the BLUENET network. You must send your API key in the request header as normal.
https://api.cloudrf.com/network?lat=38.938322&lon=1.398755&net=BLUENET&rxg=2&rxh=2
Response
The response is a verbose JSON object containing metadata about each link tested. This can be parsed to reveal the required information eg. RSSI so you can plot coloured links for example:
[
{
"Engine": "Sleipnir 1.7.8",
"Frequency MHz": 868,
"Propagation model": "Egli VHF/UHF",
"Model subtype": "Suburban",
"Receiver": [
{
"Latitude": 38.93832,
"Longitude": 1.398755,
"Ground elevation m": 91,
"Antenna height m": 2,
"Receiver gain dBd": -0.15,
"Receiver gain dBi": 2
}
],
"Transmitters": [
{
"Latitude": 38.94411,
"Longitude": 1.399756,
"Ground elevation m": 92,
"Antenna height m": 2,
"Distance to receiver km": 0.651,
"Azimuth to receiver deg": 187.65,
"Downtilt angle deg": 0.1,
"Antenna gain dBd": 0,
"Antenna gain dBi": 2.15,
"Polarisation": "Vertical",
"Power W": 1,
"Power dBm": 32.15,
"ERP W": 1,
"EIRP W": 1.641,
"ERP dBm": 30,
"EIRP dBm": 32.15,
"Free space path loss dB": 83.3,
"Bandwidth MHz": 0.1,
"Johnson Nyquist noise dB": 0.2,
"Noise floor dBm": -115,
"Channel noise dBm": -114.8,
"Signal power at receiver dBm": -87.2,
"Signal to Noise Ratio dB": 27.6,
"Computed path loss dB": 117.2,
"Model attenuation dB": 33.9,
"Field strength at receiver dBuV/m": 50.9,
"RX voltage 50 ohm dipole uV": 16,
"RX voltage 50 ohm dipole dBuV": 24,
"RX voltage 75 ohm dipole uV": 20,
"RX voltage 75 ohm dipole dBuV": 26,
"Raise RX antenna for LOS": 102,
"Raise RX antenna for fresnel 60%": 313,
"Raise RX antenna for full fresnel": 344,
"Obstructions": [
[
38.93995,
1.399037
],
[
38.94017,
1.399075
],
[
38.94039,
1.399113
],
... snipped for brevity ...
],
"Distance": [
0.012,
0.025,
0.037,
0.049,
0.062,
... snipped for brevity ...
],
"Terrain": [
102,
103,
108,
109,
112,
... snipped for brevity ...
],
"Terrain_AMSL": [
101,
102,
104,
108,
109,
... snipped for brevity ...
],
"Fresnel": [
0,
-2.04,
-2.86,
-3.47,
-3.96,
... snipped for brevity ...
],
"dBm": [
-21,
-31,
-38,
-42,
-46,
... snipped for brevity ...
],
"dB": [
51,
61,
68,
72,
76,
... snipped for brevity ...
]
}
],
"Chart image": "https://api.cloudrf.com/API/archive/data?ppa=5d0ffce3&uid=1",
"Network KML": "https://api.cloudrf.com/users/1/b7ce7077.kml",
"Server ID": 8201612,
"Server name": "0816143545_BLUENET_Site"
},
{
"Engine": "Sleipnir 1.7.8",
"Frequency MHz": 868,
"Propagation model": "Egli VHF/UHF",
"Model subtype": "Suburban",
"Receiver": [
{
"Latitude": 38.93832,
"Longitude": 1.398755,
"Ground elevation m": 91,
"Antenna height m": 2,
"Receiver gain dBd": -0.15,
"Receiver gain dBi": 2
}
],
"Transmitters": [
{
"Latitude": 38.94279,
"Longitude": 1.386807,
"Ground elevation m": 117,
"Antenna height m": 2,
"Distance to receiver km": 1.148,
"Azimuth to receiver deg": 115.66,
"Downtilt angle deg": 1.3,
"Antenna gain dBd": 0,
"Antenna gain dBi": 2.15,
"Polarisation": "Vertical",
"Power W": 1,
"Power dBm": 32.15,
"ERP W": 1,
"EIRP W": 1.641,
"ERP dBm": 30,
"EIRP dBm": 32.15,
"Free space path loss dB": 88.3,
"Bandwidth MHz": 0.1,
"Johnson Nyquist noise dB": 0.2,
"Noise floor dBm": -115,
"Channel noise dBm": -114.8,
"Signal power at receiver dBm": -96.9,
"Signal to Noise Ratio dB": 17.9,
"Computed path loss dB": 126.9,
"Model attenuation dB": 38.6,
"Field strength at receiver dBuV/m": 41.3,
"RX voltage 50 ohm dipole uV": 5,
"RX voltage 50 ohm dipole dBuV": 14,
"RX voltage 75 ohm dipole uV": 6,
"RX voltage 75 ohm dipole dBuV": 16,
"Raise RX antenna for LOS": 80,
"Raise RX antenna for fresnel 60%": 92,
"Raise RX antenna for full fresnel": 100,
"Obstructions": [
[
38.93835,
1.398676
],
[
38.93843,
1.398467
],
[
38.93847,
1.398363
]
... snipped for brevity ...
],
"Distance": [
0.01,
0.02,
0.03,
0.04,
0.05,
... snipped for brevity ...
],
"Terrain": [
111,
111,
110,
110,
110,
... snipped for brevity ...
],
"Terrain_AMSL": [
114,
112,
112,
111,
111,
... snipped for brevity ...
],
"Fresnel": [
0,
-1.85,
-2.61,
-3.18,
-3.65,
... snipped for brevity ...
],
"dBm": [
-16,
-27,
-34,
-39,
-43,
... snipped for brevity ...
],
"dB": [
46,
57,
64,
69,
73,
... snipped for brevity ...
]
}
],
"Chart image": "https://api.cloudrf.com/API/archive/data?ppa=3a949f40&uid=1",
"Network KML": "https://api.cloudrf.com/users/1/b7ce7077.kml",
"Server ID": 8201611,
"Server name": "0816143541_BLUENET_Site"
}
]
HF Area
The /hf/area
endpoint accepts a JSON object in the request body describing your network and will run a point-to-multipoint “heatmap” calculation using VOACAP.
Request
The below example is for a 4MHz signal at 00:00 UTC in July. The request is sent as a POST
request to https://api.cloudrf.com/hf/area
and it will return a JSON response containing metadata and URLs to image layers for your map.
{
"site": "Test",
"network": "HFTest",
"transmitter": {
"lat": 51.833992,
"lon": -2.231319,
"frq": 4.0
},
"antenna": {
"txg": 2.0,
"txl": 0.0,
"ant": 1,
"pol": "h",
"azi": 90
},
"model": {
"month": 7,
"hour": 0,
"rel": 50
},
"output": {
"col": "SNR1.dB",
"bounds": {
"north": "72.499717",
"east": "40.944650",
"south": "6.353099",
"west": "-60.400436"
},
"nf": -140,
"out": 4
}
}
Response
The PNG_Mercator
image is warped for slippy maps like Mapbox, Leaflet and Google Maps. The PNG_WGS84
is for globes like Google Earth, Cesium and WinTAK.
{
"kmz": "https://api.cloudrf.com/archive/M2JUbU5yOU5XNFFWZ29JZjNmaFFUdz09/kmz",
"PNG_Mercator": "https://api.cloudrf.com/output/projection/3857/1/1017080906_HFTest_Test_HF",
"PNG_WGS84": "https://api.cloudrf.com/users/1/1017080906_HFTest_Test_HF.4326.png",
"bounds": [
72.499717,
40.94465,
6.353099,
-60.400436
],
"id": 2025,
"sid": "M2JUbU5yOU5XNFFWZ29JZjNmaFFUdz09",
"key": [
{
"l": "40dB",
"r": 37,
"g": 131,
"b": 255
},
{
"l": "30dB",
"r": 49,
"g": 254,
"b": 127
},
{
"l": "20dB",
"r": 218,
"g": 254,
"b": 60
},
{
"l": "10dB",
"r": 254,
"g": 72,
"b": 72
}
],
"elapsed": 2364.0,
"balance": 10000000000
}
PNG image with colours which map to the signal levels in the JSON output:
HF Prediction
The /hf/prediction
endpoint accepts a JSON object in the request body describing your network and will run a point-to-point calculation using VOACAP.
It produces an output for multiple frequencies, and so can be used for frequency selection.
Request
The below example is for a 4MHz signal in March. The request is sent as a POST
request to https://api.cloudrf.com/hf/prediction
and it will return a JSON response containing a link to a chart png.
{
"site": "Test",
"network": "HFTest",
"transmitter": {
"lat": "51.8656",
"lon": "-2.207",
"frq": 4.0,
"txw": 10,
"alt": 6.0
},
"receiver": {
"lat": "41.86",
"lon": "12.78",
"rxg": "2.15"
},
"antenna": {
"txg": 2.15,
"ant": 2
},
"model": {
"month": 3,
"sunspots_r12": 50
},
"output": {
"nf": -140
}
}
Response
{
"Transmitters": [
{
"Latitude": 51.8656,
"Longitude": -2.207
}
],
"Receiver": [
{
"Latitude": 41.86,
"Longitude": 12.78
}
],
"Chart image": "https://api.cloudrf.com/users/1/1029125124_HFTest_Test_HF_PPA.png",
"elapsed": 1295.0,
"balance": 10000000000,
"metadata": {
"locations": {
"transmitter": {
"latitude": 51.8656,
"longitude": -2.207,
"altitude_m": 6.0
},
"receiver": {
"latitude": 41.86,
"longitude": 12.78
},
"distance_km": 1586.7
},
"antenna": {
"name": "ITSA-1 Horizontal Dipole",
"gain": 2.15
},
"month": "March",
"power_w": 10.0
},
"hours": {
"hour0": [
{
"frequency": 2.0,
"snr": 14.0
},
{
"frequency": 5.0,
"snr": 22.0
},
{
"frequency": 8.0,
"snr": 27.0
},
{
"frequency": 11.0,
"snr": 7.0
},
{
"frequency": 14.0,
"snr": -32.0
},
{
"frequency": 17.0,
"snr": -77.0
},
{
"frequency": 20.0,
"snr": -76.0
},
{
"frequency": 4.0,
"snr": 20.0
}
],
"hour1": [
{
"frequency": 2.0,
"snr": 17.0
},
{
"frequency": 5.0,
"snr": 26.0
},
{
"frequency": 8.0,
"snr": 30.0
},
{
"frequency": 11.0,
"snr": -6.0
},
{
"frequency": 14.0,
"snr": -74.0
},
{
"frequency": 17.0,
"snr": -74.0
},
{
"frequency": 20.0,
"snr": -73.0
},
{
"frequency": 4.0,
"snr": 24.0
}
],
...snipped for brevity...
}
}
Chart image showing which frequencies are stronger at different times of day:
Area API with bounded output
Often the study area is far away from the transmitter, for example a suburb which is served by a mountain repeater. By bounding the output area in the request, only this focus area is computed and returned.
Aside from being very fast and efficient, it allows for higher resolution calculations than would be possible by testing the entire town / valley in all directions.
For example, to test an airfield with LiDAR accuracy that is 42km away from the transmitter you would populate the “bounds” array within the “output” section of an area request.
{
... snipped for brevity ...
"output": {
"units": "m",
"col": "3",
"out": "2",
"ber": "1",
"mod": null,
"nf": "-120",
"res": "2",
"rad": "42",
"bounds": {
"north": 51.677,
"south": 51.655,
"east": -2.039,
"west": -2.076
}
}
}
More Information
For a complete OpenAPI 3 schema of the CloudRF API please consult the Swagger UI documentation.
Compression
CloudRF uses gzip compression for output files. The official client handles this but if you are writing your own you should use the MIME type rather than writing raw data as a .tiff
may actually be .tiff.gz
.
Below shows an example of this in Python 3:
# WARNING: Will write a gzipped file
shutil.copyfileobj(response.raw, outputFile)
# Will handle gzip decompression
outputFile.write(response.content)
Verbose Environment Variables
Since v3.8
, the environment
block has been redesigned to replace the cryptic cll
and clm
values with simpler boolean layers describing elevation
, landcover
, buildings
and obstacles
. This enables easier layering of custom clutter upon DTM for example (elevation = 2
, obstacles = 1
).
Both methods are supported in 3.8
but the long form is now the standard, as used in the web interface, and the old trigraph method is deprecated. It still features in places such as templates.
The following table translates old to new values:
Legacy Value |
New Value (3.8) |
---|---|
cll = 0 |
landcover = 0 |
cll = 1 |
landcover = 1 |
cll = 2 |
landcover = 1, buildings = 1 |
clm = 0 |
obstacles = 0 |
clm = 1 |
obstacles = 1 |
clm = 2 |
elevation = 2, obstacles = 1 |
API scripts
A list of ready-to-use scripts are available on the CloudRF public GitHub repository.
Some radio templates are here to help you pick good settings for hardware: