# 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](https://cloudrf.com/documentation/developer/). ## 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](https://learning.postman.com/docs/sending-requests/authorization/#api-key) allows you to enter your key from their interface. ## API "Hello World!" Send this curl request to model coverage for a VHF radio, 2m above the water with 1W of power: ```json 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](https://cloudrf.com/documentation/developer/). 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. 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. 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. #### Request ```json { "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 ```json { "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: ![Area demo](Images/API/area_demo.png) #### Go faster with the GPU engine Switch the engine parameter to 1 to use the GPU engine. All other settings are the same. The GPU engine does not support the ITM model (pm: 1) at the time of writing so will replace it with the simpler Egli model (pm: 11) if ITM is requested. ``` { "site": "Harbour", "network": "PMR", "engine": "1", "transmitter": { ... ``` #### 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. ```json { "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" } } ``` ![Antennas demo](Images/API/antennas_demo.jpg) ### Path The `path` endpoint accepts a JSON object describing your network and will run a point-to-point calculation. 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`. #### Request ```json { "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 Plotly or an equivalent library. It also contains a link to a KMZ so you can see the profile in 3D. ```json { "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" } ``` ![Path demo](Images/API/pathprofile_chart.jpg) ### Points The `points` endpoint accepts a JSON object describing an array of points (transmitters) which will be tested back to a single point (receiver). The following example is for route a boat will take. The location varies with each point but all the other values are constant. #### Request The request is sent as a `POST` body to `https://api.cloudrf.com/points`. ```JSON { "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. ```json { "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" } ``` ![Points demo](Images/API/route_demo.jpg) ### 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. 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. ```json { "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 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. ```json { "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. 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: ![BSA demo](Images/API/bsa_demo.jpg) #### 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. ```json { "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" }, ... ``` By defining 3 points, we have reduced the study area to a triangle: ![BSA edges demo](Images/API/bsa_edges_demo.png) ### 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**. Unlike the `Area` API, you cannot use arrays of azimuths with this API, you must define each required azimuth separately as a distinct transmitter. #### 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. 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. ```json { "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 ```json { "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 } ``` ![Multisite demo](Images/API/multisite_demo.png) ### 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: ```json { "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 ] } ``` ![Mesh demo](Images/API/mesh_demo.png) ### 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. **It will be upgraded with a GPU capability.** #### 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: ```json { "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 ] } ``` ![QRM demo](Images/API/interference_demo.png) ### 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: ```json [ { "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" } ] ``` ![Network demo](Images/API/network_demo.jpg) ## More Information See more example requests and responses on the official API documentation on the Postman network at [https://docs.cloudrf.com](https://docs.cloudrf.com). For a complete OpenAPI 3 schema of the CloudRF API please consult the [Swagger UI documentation](https://cloudrf.com/documentation/developer/). ### 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 Python3: ``` # 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,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](https://github.com/Cloud-RF/CloudRF-API-clients/). - [Python](https://github.com/Cloud-RF/CloudRF-API-clients/tree/master/python) - [Bash](https://github.com/Cloud-RF/CloudRF-API-clients/tree/master/bash) - [Slippy maps](https://github.com/Cloud-RF/CloudRF-API-clients/tree/master/slippy-maps) Some radio templates [are here](https://github.com/Cloud-RF/CloudRF-API-clients/tree/master/templates) to help you pick good settings for hardware: