Adeunis field tester with TTNMapper

Good morning TTN-Fellows,

I would like to use the Adeunis field tester:
https://www.adeunis.com/en/produit/ftd-network-tester/

along with TTNMapper. What is the recommended procedure right now? Is turning on the TTNMapper integration sufficient or should I use the payload decoder developed here:
https://www.thethingsnetwork.org/labs/story/payload-decoder-for-adeunis-field-test-device-ttn-mapper-integration

The latest discussion is already two years old:

Thanks for your time guys!

I have run a quick test and did not find that TTN Mapper is using Adeunis data.
The story you refer to is very incomplete. We might ask @jpmeijers for an answer.

Hi @ecosoph. You will need the payload decoder configured on the TTN Console. Then if you enable the TTN Mapper integration the parsed payload fields will be sent to TTN Mapper. If you don’t have a payload decoder configured that spits out latitude and longitude, TTN Mapper does not know what to do with the data and discards it.

A quick glance over that parser you linked. It should work, but the quality of the code doesn’t look great. I see a lot of conversion between numbers to strings and back to numbers.

1 Like

I have added the payload.lat and payload.lon to the custom payload-script and added TTN-Mapper integration to the application and it works fine:

image

This is the modified code:

// 24-4-2020, remko@rfsee.nl, Modification to add payload.lat en lon to make compatible with TTN

function Decoder( bytes, port ){

   // Functions

   function parseCoordinate( raw_value, coordinate ){
      // This function parses a coordinate payload part into 
      
      // dmm and ddd 
      var raw_itude = raw_value;
      var temp   = "";
      
      // Degree section
      var itude_string = ( (raw_itude >> 28) & 0xF ).toString( );
      raw_itude <<= 4;
      itude_string += ( (raw_itude >> 28) & 0xF ).toString( );
      raw_itude <<= 4;
      coordinate.degrees += itude_string;
      itude_string += "°";
      
      // Minute section
      temp   = ( (raw_itude >> 28) & 0xF ).toString( );
      raw_itude <<= 4;
      temp   += ( (raw_itude >> 28) & 0xF ).toString( );
      raw_itude <<= 4;
      itude_string += temp;
      itude_string += ".";
      coordinate.minutes += temp;
      
      // Decimal section
      temp   = ( (raw_itude >> 28) & 0xF ).toString( );
      raw_itude <<= 4;
      temp += ( (raw_itude >> 28) & 0xF ).toString( );
      raw_itude <<= 4;
      itude_string += temp;
      coordinate.minutes += ".";
      coordinate.minutes += temp;
      return itude_string;
   }

   function parseLatitude( raw_latitude, coordinate ){
      var latitude = parseCoordinate( raw_latitude, coordinate );
      latitude      += ((raw_latitude & 0xF0) >> 4).toString( );
      coordinate.minutes   += ((raw_latitude & 0xF0) >> 4).toString( );
      return latitude;
   }

   function parseLongitude( raw_longitude, coordinate ){
      var longitude = ( ((raw_longitude >> 28) & 0xF )  ).toString( );
      coordinate.degrees   = longitude;
      longitude   += parseCoordinate( raw_longitude << 4, coordinate );
      return longitude;
   }

   function addField(field_no, payload){
      switch(field_no){
      // Presence of temperature information
         case 0: 
            payload.temperature = bytes[bytes_pos_] & 0x7F;
            // Temperature is negative
            if((bytes[bytes_pos_] & 0x80) > 0 ){
               payload.temperature -= 128;
            }
            bytes_pos_++;
            break;

         // Transmission triggered by the accelerometer
         case 1: 
            payload.trigger = "accelerometer";
            break;

         // Transmission triggered by pressing pushbutton 1
         case 2: 
            payload.trigger = "pushbutton";
            break;

         // Presence of GPS information
         case 3: 
            // GPS Latitude
            // An object is needed to handle and parse coordinates into ddd notation
            var coordinate     = {};
            coordinate.degrees = "";
            coordinate.minutes = "";

            var raw_value = 0;
            raw_value    |= bytes[bytes_pos_++] << 24;
            raw_value    |= bytes[bytes_pos_++] << 16;
            raw_value    |= bytes[bytes_pos_++] << 8;
            raw_value    |= bytes[bytes_pos_++];

            payload.lati_hemisphere = (raw_value & 1) == 1 ? "South" : "North";
            payload.latitude_dmm    = payload.lati_hemisphere.charAt( 0 ) + " ";
            payload.latitude_dmm   += parseLatitude( raw_value, coordinate );
            payload.latitude        = ( parseFloat( coordinate.degrees ) + parseFloat( coordinate.minutes ) / 60 ) * ( (raw_value & 1) == 1 ? -1.0 : 1.0);
            payload.lat = payload.latitude;

            // GPS Longitude
            coordinate.degrees = "";
            coordinate.minutes   = "";

            raw_value    = 0;
            raw_value   |= bytes[bytes_pos_++] << 24;
            raw_value   |= bytes[bytes_pos_++] << 16;
            raw_value   |= bytes[bytes_pos_++] << 8;
            raw_value   |= bytes[bytes_pos_++];

            payload.long_hemisphere = (raw_value & 1) == 1 ? "West" : "East";
            payload.longitude_dmm   = payload.long_hemisphere.charAt( 0 ) + " ";
            payload.longitude_dmm  += parseLongitude( raw_value, coordinate );
            payload.longitude       = (parseFloat(coordinate.degrees) + parseFloat(coordinate.minutes)/60) * ((raw_value & 1) == 1 ? -1.0 : 1.0);
            payload.lon = payload.longitude;

            // GPS Quality
            raw_value = bytes[bytes_pos_++];
            switch((raw_value & 0xF0) >> 4 ){
               case 1: 
                  payload.gps_quality = "Good";
                  break;
                  
               case 2: 
                  payload.gps_quality = "Average";
                  break;
                  
               case 3: 
                  payload.gps_quality = "Poor";
                  break;
                  
               default:
                  payload.gps_quality = (raw_value >> 4) & 0xF;
                  break;
            }
            payload.hdop   = (raw_value >> 4) & 0xF;

            // Number of satellites
            payload.sats   = raw_value & 0xF;
            break;

         // Presence of Uplink frame counter
         case 4: 
            payload.ul_counter = bytes[bytes_pos_++];
            break;

         // Presence of Downlink frame counter
         case 5: 
            payload.dl_counter = bytes[bytes_pos_++];
            break;

         // Presence of battery level information
         case 6: 
            payload.battery_level = bytes[bytes_pos_++] << 8;
            payload.battery_level |= bytes[bytes_pos_++];
            break;

         // Presence of RSSI and SNR information
         case 7: 
            // RSSI
            payload.rssi_dl   = bytes[bytes_pos_++];
            payload.rssi_dl   *= -1;

            // SNR
            payload.snr_dl   = bytes[bytes_pos_] & 0x7F;
            if( (bytes[bytes_pos_] & 0x80) > 0 ){
               payload.snr_dl   -= 128;
            }
            bytes_pos_++;
            break;

         default:
            // Do nothing
            break;
      }
   }

   // Declaration & initialization
   var status_ = bytes[0];
   var bytes_len_ = bytes.length;   
   var bytes_pos_ = 1;
   var i = 0;
   var payload = {};

   // Get raw payload
   var temp_hex_str = ""
   payload.payload  = "";
   for( var j = 0; j < bytes_len_; j++ ){
      temp_hex_str   = bytes[j].toString( 16 ).toUpperCase( );
      if( temp_hex_str.length == 1 ){
         temp_hex_str = "0" + temp_hex_str;
      }
      payload.payload += temp_hex_str;
   }

   // Get payload values
   do{
      // Check status, whether a field is set
      if((status_ & 0x80) > 0 ){
         addField(i, payload);
      }
      i++;
   }while(((status_ <<= 1) & 0xFF) > 0 );

   return payload;

}