Collos Location

I’ve been playing with this as well this week. When i used the Lora Skyhook API in the integrations in the TTN console, nothing was received in cayenne. It worked when i used the LoraWifi API (https://api.preview.collos.org/localization-lora-recipes/v1/loraWifi).

The whole thing is work-in-progress, so maybe the skyhook api is not up yet.

The TTN guys are looking into it. The Collos endpoint is working from e.g. Node Red, so we need to chase down where it is broken.

Sorry for the delay folks.

2 Likes

Hi all,

As @RichL said it seems that there is some issues with the integration, but in the mean time you can use Node-Red.
Here is a flow example :slight_smile:

[{“id”:“6e1ad3d3.689614”,“type”:“function”,“z”:“10b053c.e54872c”,“name”:“Prepare Query”,“func”:“msg.headers = {};\nmsg.headers[‘Ocp-Apim-Subscription-Key’] = ‘YOUR_API_KEY’;\nmsg.headers[‘Content-Type’] = ‘application/json’;\n\nvar access_points = msg.ttn.payload_fields.access_points;\nvar gateways = msg.ttn.metadata.gateways;\nmsg.payload = ;\n\n// Extract fine timestamps\nfor ( var i = 0; i < gateways.length; i++ ) {\n // Build gateway\n var gateway = {\n ‘gatewayId’: gateways[i].gtw_id.replace( ‘eui-’, ‘lora_’ ),\n ‘antennaId’: gateways[i].antenna,\n ‘rssi’: gateways[i].rssi,\n ‘snr’: gateways[i].snr,\n \n };\n \n // Add loc data if it exists\n if ( typeof gateways[i].latitude !== ‘undefined’ ) {\n gateway.antennaLocation = gateway.antennaLocation || {};\n gateway.antennaLocation.latitude = gateways[i].latitude;\n }\n if ( typeof gateways[i].longitude !== ‘undefined’ ) {\n gateway.antennaLocation = gateway.antennaLocation || {};\n gateway.antennaLocation.longitude = gateways[i].longitude;\n }\n if ( typeof gateways[i].altitude !== ‘undefined’ ) {\n gateway.antennaLocation = gateway.antennaLocation || {};\n gateway.antennaLocation.altitude = gateways[i].altitude;\n }\n // Add etimestamp if it exists\n if ( typeof gateways[i].fine_timestamp_encrypted !== ‘undefined’ ) {\n gateway.encryptedToa = gateways[i].fine_timestamp_encrypted;\n }\n \n msg.payload.push( gateway );\n}\n\nif ( msg.payload.length > 0 ) {\n msg.collosQuery = msg.payload;\n \n msg.payload = {\n ‘gatewayReceptions’: msg.collosQuery,\n ‘wifiAccessPoints’: access_points\n };\n\n return msg;\n}\n\n\n\n\n \n”,“outputs”:1,“noerr”:0,“x”:220,“y”:180,“wires”:[[“1919540a.625d2c”]]},{“id”:“1919540a.625d2c”,“type”:“http request”,“z”:“10b053c.e54872c”,“name”:“Collos - dev LoraSkyhook”,“method”:“POST”,“ret”:“txt”,“url”:“https://api.preview.collos.org/localization-lora-recipes/v1/loraSkyhook",“tls”:“1014c3fa.a8617c”,“x”:430,“y”:180,“wires”:[[“d3af59a1.2289d8”]]},{“id”:“d3af59a1.2289d8”,“type”:“json”,“z”:“10b053c.e54872c”,“name”:“”,“pretty”:false,“x”:610,“y”:180,“wires”:[[]]},{“id”:“1014c3fa.a8617c”,“type”:“tls-config”,“z”:“”,“name”:“Mozilla-Certificate”,“cert”:“”,“key”:“”,“ca”:“/data/ca-certificates.crt”,“certname”:“”,“keyname”:“”,“caname”:“”,"verifyservercert”:true}]

3 Likes

Sure that this is valid node red code?
I can’t import it in my node red dashboard.

It also didn’t work for me, i dug around and extracted the code from the function node:

msg.headers = {};
msg.headers['Ocp-Apim-Subscription-Key'] = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXX';
msg.headers['Content-Type'] = 'application/json';
var access_points = msg.payload_fields.access_points;
var gateways = msg.metadata.gateways;
msg.payload = [];

// Extract fine timestamps
for (var i = 0; i < gateways.length; i++ ){  
		 // Build gateway
		var gateway ={  
			'gatewayId':gateways[i].gtw_id.replace('eui-','lora_'),
			'antennaId':gateways[i].antenna,'rssi':gateways[i].rssi,
			'snr':gateways[i].snr,
			};
		  
		 
			 // Add loc data if it exists 
			if (typeof gateways[i].latitude !== 'undefined'){  
				gateway.antennaLocation = gateway.antennaLocation ||{};
				gateway.antennaLocation.latitude = gateways[i].latitude;
				}
			if (typeof gateways[i].longitude !== 'undefined'){  
				gateway.antennaLocation = gateway.antennaLocation ||{};
				gateway.antennaLocation.longitude = gateways[i].longitude;
				}
			if (typeof gateways[i].altitude !== 'undefined' ){
				gateway.antennaLocation = gateway.antennaLocation ||{};
				gateway.antennaLocation.altitude = gateways[i].altitude;
				} 
			// Add etimestamp if it exists 
			if (typeof gateways[i].fine_timestamp_encrypted !== 'undefined' ){  
				gateway.encryptedToa = gateways[i].fine_timestamp_encrypted;
				}
				
				
			msg.payload.push( gateway );
			}
if ( msg.payload.length > 0 ){  
		msg.collosQuery = msg.payload;
		  msg.payload ={  
		   'gatewayReceptions':msg.collosQuery,
		 'wifiAccessPoints':access_points};
			 return msg;
	  }

As far as i can see, when i connect a TTN input node this makes the correct syntax to sent to the API.
I direct it through a JSON node and HTTP request node and the Collos-API says:

"{"error":{  
   "code":"UnexpectedError",
   "message":"An unexpected error occured. Please try again or contact the support if the problem persists.",
   "target":null,
   "details":null,
   "innerError":null,
   "correlationId":"xxxxx-xxxxx-xxxxxx-xxxxxx-xxxxxxx"
}
}"

The forum might have interpret some characters, I put it on pastebin.

@itbv : Could you please send me the correlationId and the associated request send to Collos?

1 Like

@itbv : Sorry, my code was broken.
I extracted parts of my flow to put it in one function block for you, and forgot to change bssid & rssi names.

Here is a working (and tested) example:

[{"id":"6e1ad3d3.689614","type":"function","z":"10b053c.e54872c","name":"Prepare Query","func":"msg.headers = {};\nmsg.headers['Ocp-Apim-Subscription-Key'] = 'YOUR_API_KEY';\nmsg.headers['Content-Type'] = 'application/json';\n\nvar access_points = msg.ttn.payload_fields.access_points;\nvar gateways = msg.ttn.metadata.gateways;\nmsg.payload = [];\n\n// Extract fine timestamps\nfor ( var i = 0; i < gateways.length; i++ ) {\n    // Build gateway\n    var gateway = {\n                'gatewayId': gateways[i].gtw_id.replace( 'eui-', 'lora_' ),\n                'antennaId': gateways[i].antenna,\n                'rssi': gateways[i].rssi,\n                'snr': gateways[i].snr,\n                \n            };\n    \n    // Add loc data if it exists\n    if ( typeof gateways[i].latitude !== 'undefined' ) {\n        gateway.antennaLocation = gateway.antennaLocation || {};\n        gateway.antennaLocation.latitude = gateways[i].latitude;\n    }\n    if ( typeof gateways[i].longitude !== 'undefined' ) {\n        gateway.antennaLocation = gateway.antennaLocation || {};\n        gateway.antennaLocation.longitude = gateways[i].longitude;\n    }\n    if ( typeof gateways[i].altitude !== 'undefined' ) {\n        gateway.antennaLocation = gateway.antennaLocation || {};\n        gateway.antennaLocation.altitude = gateways[i].altitude;\n    }\n    // Add etimestamp if it exists\n    if ( typeof gateways[i].fine_timestamp_encrypted !== 'undefined' ) {\n        gateway.encryptedToa = gateways[i].fine_timestamp_encrypted;\n    }\n    \n    msg.payload.push( gateway );\n}\n\nif ( typeof access_points !== 'undefined' ) {\n    msg.payload.wifiAccessPoints = msg.ttn.payload_fields.access_points;\n    msg.payload.wifiAccessPoints.forEach( elt=> {\n        elt.macAddress = elt.bssid;\n        delete elt.bssid;\n        elt.signalStrength = elt.rssi;\n        delete elt.rssi;\n    })\n}\n\nif (  msg.payload.length > 0 ) {\n    msg.collosQuery = msg.payload;\n    \n    msg.payload = {\n        'gatewayReceptions': msg.collosQuery,\n        'wifiAccessPoints': access_points\n    };\n\n    return msg;\n}\n\n\n\n\n    \n","outputs":1,"noerr":0,"x":220,"y":180,"wires":[["1919540a.625d2c","f922d9f5.1e147"]]},{"id":"1919540a.625d2c","type":"http request","z":"10b053c.e54872c","name":"Collos - LoraSkyhook","method":"POST","ret":"txt","url":"https://api.preview.collos.org/localization-lora-recipes/v1/loraSkyhook","tls":"1014c3fa.a8617c","x":420,"y":180,"wires":[["d3af59a1.2289d8"]]},{"id":"d3af59a1.2289d8","type":"json","z":"10b053c.e54872c","name":"","pretty":false,"x":610,"y":180,"wires":[["f25df97f.4bdfa"]]},{"id":"f25df97f.4bdfa","type":"debug","z":"10b053c.e54872c","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":790,"y":180,"wires":[]},{"id":"f922d9f5.1e147","type":"debug","z":"10b053c.e54872c","name":"Query","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":430,"y":240,"wires":[]},{"id":"43b71b09.c8f014","type":"comment","z":"10b053c.e54872c","name":"Input from TTN","info":"","x":170,"y":120,"wires":[]},{"id":"1014c3fa.a8617c","type":"tls-config","z":"","name":"Mozilla-Certificate","cert":"","key":"","ca":"/data/ca-certificates.crt","certname":"","keyname":"","caname":"","verifyservercert":true}]
1 Like

Oliv, thanks for the new code, It works nice!

I’m sure my error had something to do with the JSON reformatting node being connected before the HTTP request…

One comment: I think the update of the TTN node formats the incoming message a bit different, but if you have the new version, just change:

msg.ttn.payload_fields

to

msg.payload_fields

Ok Folks,
The TTN guys traced a routing error for the LoRaSkyhook endpoint. It is all working well now. I am not 100% sure why, but i had to change the integrations settings and save again and then it sprang into life.

Thanks team TTN

I connected a TTN input node to this Node Red flow, but getting only an error as result, if the input node fires data:

“TypeError: Cannot read property ‘payload_fields’ of undefined”

I think you’re running into the same problem i had:

change:

msg.ttn.payload_fields

to

msg.payload_fields

in the function node.

i did change this already.

Is there anything special to consider for the TTN input node device, or does it work with every device? I did activate collos integration in the TTN app, where the device belongs to.

The metadata received from the TTN input node does not contain the fields needed by the function. I’m lost here :frowning:

metadata: object
time: “2018-03-14T15:17:43.267756707Z”
frequency: 867.3
modulation: “LORA”
data_rate: “SF9BW125”
airtime: 164864000
coding_rate: “4/5”
gateways: array[1]
0: object
gtw_id: “ttngw-test-ob”
gtw_trusted: true
timestamp: 1881109092
time: “2018-03-14T15:17:43Z”
channel: 4
rssi: -112
snr: -8.5
rf_chain: 0
latitude: 52.524513
longitude: 13.37022
altitude: 60
location_source: “registry”
latitude: 52.524513
longitude: 13.37022
location_source: “registry”
payload: buffer[4]raw
0: 0x0
1: 0x1f
2: 0xd
3: 0x2f
_msgid: “9e9183c5.c7774”

I think you did not integrate the payload function in the TTN console, which converts your payload into the fields that the function uses.

You can copy/paste it from here:

https://www.thethingsnetwork.org/docs/applications/collos/wifi.html

I can’t get collos running with my Adeunis RF tracker in cayenne. I setup the collos integration in my ttn app for channel 20 (collos-rssi) and channel 21 (collos-tdoa).
In cayenne i see the Adeunis Tracker device, and i the Data live view i see the incoming data on different channels. But no data on channels 20 + 21.

Any idea what’s going wrong here?

Note: There are more than 1 devices in the ttn app with the collos integration. I can’t change this, since don’t want to change the AppEUI in the Adeunis tracker (need it otherwhere).

I am happy to report that the COLLOS integration using the loraSkyhook endpoint is successfully receiving and processing my device’s WiFi scans and submitting the location result to the Cayenne integration. How cool is that! I’m preparing a demo/writeup for this project and will post in Hackster.io and update here.

1 Like

Hi @Verkehrsrot
did you solve your problem?
Rich

No. No idea.

Yeah, not so much. Not happy :neutral_face: Not working… seems like what I though was a successful WiFi localization was in fact a localization using my gateway’s fixed coords. In Cayenne this is all that is used. Ditto for the device’s location in the overview window in the TTN console. My guess is that no-one has tried WiFi yet.

Hi @RichL I’d like to get the loraSkyook API integration working end-to-end. This is the test vector - this is how it should work. The input is Skyhook’s own test vector for their location lookup service. It works in the COLLOS API tester. It doesn’t do anything in the TTN integration (just seem to get the gateway’s location in Cayenne).

  1. Use the following test vector: 000C4182D88CCE000445A0E272CE00012255A5A3CE000C41A2DF52CE

  2. Use the decoder function at https://www.thethingsnetwork.org/docs/applications/collos/wifi.html

  3. Get:
    {
    “access_points”: [
    {
    “bssid”: “00:0c:41:82:d8:8c”,
    “rssi”: -50
    },
    {
    “bssid”: “00:04:45:a0:e2:72”,
    “rssi”: -50
    },
    {
    “bssid”: “00:01:22:55:a5:a3”,
    “rssi”: -50
    },
    {
    “bssid”: “00:0c:41:a2:df:52”,
    “rssi”: -50
    }
    ]
    }

  4. Dial up Cayenne, or the Device location view (the position is supposed to be reported here too, right?), and get:
    https://www.google.ca/maps/place/42.2961366,-71.2368669