Kyneo SD Data Logger

Introduction

In some applications, besides or instead of sending the data obtained by the Kyneo, it might be useful to store it. That’s why Kyneo integrates a micro SD card slot. This storage solutions provides modular expansion for the board and ensures that all the data can be safely stored. It must be noted that the speed of data storing will highly depend on the SD card installed. We recommend a high quality Class 10 SD card.

DataLogger Description

The following program, takes all the data from the Kyneo Sensors and stores it on a micro SD card. Actually, this is a combination of the programs in the examples before that calls the SD card instead of printing the data through the Serial port. It will print them into a file named File_write. This program might be used as a kinematic data logger since it will record the attitude and position of the Kyneo at a given time at a rate of several times per second.

Example Sketch Code

/**************************************************************************************************
 * Kyneo data logger Example
 * 
 * This Example will show how to record data from the kyneo sensors on a mounted SD card
 * 
 * Created by GEKO Navsat S.L. for Kyneo V2.0 board
 * 
 * This example is free software, given under a GPL3 license
 * 
 * KYNEO is a product designed by GEKO Navsat S.L. in Europe http://www.gekonavsat.com
 * For further information, visit: http://kyneo.eu/
 * 
 *************************************************************************************************/

#include <KyneoBoard.h>                                                                                     // GekoNavsat libraries
#include <KyneoGNSS.h>
#include <KyneoUtils.h>
#include <FreeIMU.h>

#include                                                                                             // Other libraries
#include 

FreeIMU kyneoIMU;                                                                                           // Object definition
KyneoGNSS gnss;         
KyneoUtils util;   

float att[3];                                                                                               // Variable definition
float kyneoLat;
float kyneoLon;
float kyneoAlt;
char kyneoTime[10];

char file[12];

void setup()
{  
  Serial.begin(9600);                                                                                       // Initial setup
  delay(100);
  Serial.println("Setting up... ");
 
  pinMode(GP_LED_0, OUTPUT);
  pinMode(GP_LED_1, OUTPUT);
  
  Wire.begin();
  kyneoIMU.init();
  
  Serial1.begin(9600);
  delay(100);
  //set rate divider(GPGLL, GPRMC, GPVTG, GPGGA, GPGSA, GPGSV, PMTKCHN). 
  gnss.setNMEAOutput(    0,     0,     0,     1,     0,     0,       0);                                     // Period of each NMEA message
  //gnss.DisSBAS();
  //gnss.setFixRate(100); //milliseconds (100 - 10000)

  delay(100);
  
  uint8_t retries = 3;
  uint8_t filenum = 0;
  while(retries-- > 0){
    if (!SD.begin(SD_CS_PIN)) {                                                                              // SD card setup
      Serial.println("SD card failed, or not present");                                                   
    }
    else{
      Serial.println("SD card ready.");
      do{
        sprintf(file, "data%03u.txt", filenum++);      
      }
      while(SD.exists(file));   
      Serial.print("Data will be logged to file: ");
      Serial.println(file);
      break;
    }
  }
}

void loop()
{
  if(gnss.getLatLon(kyneoLat, kyneoLon) == 2){                                                               // Write the GNSS and IMU data if abailable
     
    gnss.gettime(kyneoTime);
    kyneoAlt = gnss.getalt();   
    kyneoIMU.getYawPitchRoll(att);

    File log = SD.open(file, FILE_WRITE);
    if(log == 0){
      Serial.println("Error logging data: log = 0");
    }else{
      
      digitalWrite(GP_LED_0, HIGH);
      
      log.print(att[0]);
      log.print(",");
      log.print(att[1]);
      log.print(",");
      log.print(att[2]);
      log.print(",");
      log.print(kyneoLat,6);
      log.print(",");
      log.print(kyneoLon,6);
      log.print(",");
      log.print(kyneoAlt);
      log.print(",");
      log.print(kyneoTime);
      log.println(";");
      
      log.close();
      Serial.println("IMU and GNSS data logged");
  
      digitalWrite(GP_LED_0, LOW);
    }
  }else{
                                                                                                             // Write the IMU data only if GNSS is not abailable
    kyneoIMU.getYawPitchRoll(att);

    File log = SD.open(file, FILE_WRITE);
    if(log == 0){
      Serial.println("Error logging data: log=0");
    }else{
      
      digitalWrite(GP_LED_1, HIGH);
          
      log.print(att[0]);
      log.print(",");
      log.print(att[1]);
      log.print(",");
      log.print(att[2]);
      log.println(",,,,;");
      
      log.close();
      Serial.println("IMU data logged, no GNSS data");
      
      digitalWrite(GP_LED_1, LOW);
    }
  }
  
  delay(100);
}

Link to software repository

More information about Kyneo go to Kyneo Manual

For all the latest software visit our GitHub page GEKO Navsat Github's examples

Kyneo Utilities

Introduction

In this tutorial we will show how to program some aditional utilities for the the Kyneo: Battery voltage reading, IMU sensors speed test and a test bench.

Battery voltage and temperature description

The Kyneo board is powered by a Lithium-Ion battery, this battery has a maximum voltage of 4.2 V and a minimum of 3V, since the relation between the battery’s charge and it’s voltage is known, this is a way of knowing the state of charge of the cell.

The temperature is measured by the barometer, and it is measured on the board, this might or might not match the ambient temperature.

The following program prints the voltage and temperature through the serial port.

Example Sketch Code – Voltage and Temperature

/**************************************************************************************************
 * Kyneo Voltage and Temperature Example
 * 
 * This sketch will show the board temperature as well as the battery voltage through the Serial 
 * monitor
 * 
 * 
 * Created by GEKO Navsat S.L. for Kyneo V2.0 board
 * 
 * This example is free software, given under a GPL3 license
 * 
 * KYNEO is a product designed by GEKO Navsat S.L. in Europe http://www.gekonavsat.com
 * For further information, visit: http://kyneo.eu/
 * 
 *************************************************************************************************/
#include <KyneoBoard.h>                                 //GekoNavsat libraries
#include <FreeIMU.h>
#include <KyneoUtils.h>

KyneoUtils util;                                        //Object definition
FreeIMU kyneoIMU;

int LoopRate = 3000;  //milliseconds                 //Variable definition


void setup()
{
  Serial.begin(9600);                                                        //Initialization
  
  util.battInit();
  Wire.begin();
  kyneoIMU.init();

  delay(100);
  Serial.println("Setup done");
  delay(100);
  Serial.println("Remember to connect the battery \n");

  util.battLevel(); //first unreal read
}


void loop()
{
  Serial.print("Battery level (mV): ");
  Serial.println(util.battLevel());                                          //Data adquisition and display
  Serial.print("Board Temperature (Cº): ");
  Serial.print(kyneoIMU.baro.getTemperature(MS561101BA_OSR_4096), 1); 
  Serial.println("\n");
  
  delay(LoopRate);
}

IMU Speed Test

The following sketch performs a speed test of three operations: reading the sensor, calibrate the sensors, and sensor fusion. Three complex operations that are measured in milliseconds.

Example Sketch Code – IMU Speed Test

/**************************************************************************************************
 * Kyneo IMU speed test example
 * 
 * This sketch will perform a speed test of the sensor reading runtime, it will show the reading
 * time of the IMU, sensor calibration and sensor fusion
 * 
 * Created by GEKO Navsat S.L. for Kyneo V2.0 board
 * 
 * This example is free software, given under a GPL3 license
 * 
 * KYNEO is a product designed by GEKO Navsat S.L. in Europe http://www.gekonavsat.com
 * For further information, visit: http://kyneo.eu/
 * 
 *************************************************************************************************/
#include <KyneoBoard.h>                                    // GekoNavsat libraries
#include <FreeIMU.h>

FreeIMU kyneoIMU;                                          // Object definition

int LoopRate = 5000;  //milliseconds                       // Variable definition
int raw[11]; 
char str[512];
float val[9], q[4];
unsigned long start, stop;


void setup() { 
  
  Serial.begin(9600);                                      // Initial setup
  Serial.println("Kyneo IMU velocity test");
  
  Wire.begin();
  kyneoIMU.init();

  Serial.println("Setup done\n");
}

void loop() {
  
  Serial.println("Testing RAW reading speed:");
  start = micros();
  for(int i=0; i<1000; i++) {                              // Getting values
    kyneoIMU.getRawValues(raw);
  }
  stop = micros();
  Serial.print("--> result: ");
  Serial.print((stop - start) / 1024);
  Serial.print(" microseconds -- ");
  Serial.print(((stop - start) / 1024.0) / 1000.0);        // Write values
  Serial.println(" milliseconds");
  
  
  Serial.println("Testing CALIBRATED reading speed");
  start = micros();
  for(int i=0; i<1024; i++) {
    kyneoIMU.getValues(val);
  }
  stop = micros();
  Serial.print("--> result: ");
  Serial.print((stop - start) / 1024);
  Serial.print(" microseconds --");
  Serial.print(((stop - start) / 1024.0) / 1000.0);
  Serial.println(" milliseconds");
  
  
  Serial.println("Testing SENSOR FUSION speed:");
  start = micros();
  for(int i=0; i<1024; i++) {
    kyneoIMU.getQ(q);
  }
  stop = micros();
  Serial.print("--> result: ");
  Serial.print((stop - start) / 1024);
  Serial.print(" microseconds -- ");
  Serial.print(((stop - start) / 1024.0) / 1000.0);
  Serial.println(" milliseconds");
  
  Serial.println("Test finished, it will be performed again");
  Serial.println("----");
  
  delay(LoopRate);
}

Kyneo Test Bench

This program is a testing rig for the Kyneo’s sensors, it will provide a reading from the sensor when asked for it. This might be one of the most useful programs for the board. It can be used as a way to show the boards capabilities but also as a way of testing any of the sensors. This program is controlled by single letter commands, in order for it to work properly we have to set the input to “no line adjustment”.

Example Sketch Code – Kyneo Test Bench

/**************************************************************************************************
 * Kyneo test bench Example
 * 
 * This serves as a test bench in order to check the readings of the boards sensors, the imput
 * commands are the following:
 * 
 * A.- Euler Angles values
 * Q.- Quaternion values
 * R.- Raw IMU sensors data
 * B.- Battery Voltage
 * L.- Blinking LEDs
 * G.- Location and time information
 * N.- Last received NMEA frame
 * 
 * 
 * Created by GEKO Navsat S.L. for Kyneo V2.0 board
 * 
 * This example is free software, given under a GPL3 license
 * 
 * KYNEO is a product designed by GEKO Navsat S.L. in Europe http://www.gekonavsat.com
 * For further information, visit: http://kyneo.eu/
 * 
 *************************************************************************************************/
#include <KyneoBoard.h>                                                           // GekoNavsat libraries
#include <KyneoGNSS.h>
#include <KyneoUtils.h>
#include <FreeIMU.h>

FreeIMU kyneoIMU;                                                                 // Object definition
KyneoGNSS gnss;         
KyneoUtils util;  

int LoopRate = 10;  //milliseconds                                                // Variable definition
int alt;
int gnss_idx = 0, newFrame = 0, options = 0, count = 0;
int headprint = 10, counter = 9, ledFactor = 10;                                  // If you want to read the hidden message, set ledFactor to 1
int raw[9];
float lat, lon;
float att[3],q[4];
char cmd, a;
char frame[100], day[10], hour[10];

void setup()
{
  Serial.begin(9600);
  Serial.print("Setting up... ");

  util.battInit();
  Wire.begin();
  kyneoIMU.init();
  pinMode(GP_LED_0, OUTPUT);
  pinMode(GP_LED_1, OUTPUT);

  Serial1.begin(9600);  // Default baudrate to connect with GNSS module
  delay(100);
  //set rate divider(GPGLL, GPRMC, GPVTG, GPGGA, GPGSA, GPGSV, PMTKCHN). 
  gnss.setNMEAOutput(    0,     1,     0,     1,     0,     0,       0);         // Period of each NMEA message
                                                                          
  Serial.println("done!");
  Serial.println("--------------------------------------------");
  Serial.println("Welcome to the Kyneo testbench by GekoNavsat!");
}

void loop() {
  if(options == 0){
    Serial.println();
    Serial.println("Choose one of the following options:");
    Serial.println("    ");
    Serial.println("    H.- Hello");
    Serial.println("    A.- Euler Angles values");
    Serial.println("    Q.- Quaternion values");
    Serial.println("    R.- Raw IMU sensors data");
    Serial.println("    B.- Battery Voltage");
    Serial.println("    L.- Blinking LEDs");
    Serial.println("    G.- Location and time information");
    Serial.println("    N.- Last received NMEA frame");
    Serial.println("    - Send any command to stop");
    Serial.println();
    Serial.println("*commands must be sent with -NO LINE ENDING-");
    Serial.println();
    
    while(!Serial.available());                                                   // Waits for user command
    while(Serial.available()){
      if(count++ == 0)cmd = Serial.read();
    }
    count = 0;
    options = 1;
  }else{

    if(cmd == 'h'|| cmd == 'H') {
      Serial.println("Hello user!");
      
    }else if (cmd == 'a' || cmd == 'A'){
      while(!Serial.available()){
        kyneoIMU.getYawPitchRoll(att);
        Serial.print("Yaw: ");
        Serial.print(att[0]);
        Serial.print(" Pitch: ");
        Serial.print(att[1]);
        Serial.print(" Roll: ");
        Serial.print(att[2]);
        Serial.println("");
      }
      
    }else if (cmd == 'q' || cmd == 'Q'){

      while(!Serial.available()){
        kyneoIMU.getQ(q);
        Serial.print("  q0: ");
        Serial.print(q[0]);
        Serial.print("  q1: ");
        Serial.print(q[1]);
        Serial.print("  q2: ");
        Serial.print(q[2]);
        Serial.print("  q3: ");
        Serial.println(q[3]);
      }
      
    }else if (cmd == 'r' || cmd == 'R'){
      while(!Serial.available()){
        counter++;
        if(counter == 10){                                                      // Prints column titles every 10 lines
          counter = 0;
          Serial.println("acc_x\tacc_y\tacc_z\tgyr_x\tgyr_y\tgyr_z\tmag_x\tmag_y\tmag_z\tTemp\tAlt");  
        }
  
        kyneoIMU.getRawValues(raw);
        Serial.print((float)raw[0]/2048, 2);    // acc_x
        Serial.print("\t");
        Serial.print((float)raw[1]/2048, 2);    // acc_y
        Serial.print("\t");
        Serial.print((float)raw[2]/2048, 2);    // acc_z
        Serial.print("\t");
        Serial.print((float)raw[3]/16.384, 2);  //gyr_x
        Serial.print("\t");
        Serial.print((float)raw[4]/16.384, 2);  //gyr_y
        Serial.print("\t");
        Serial.print((float)raw[5]/16.384, 2);  //gyr_z
        Serial.print("\t");
        Serial.print((float)raw[6]/1.707, 1);   //mag_x
        Serial.print("\t");
        Serial.print((float)raw[7]/1.707, 1);   //mag_y
        Serial.print("\t");
        Serial.print((float)raw[8]/1.707, 1);   //mag_z
        Serial.print("\t");
        Serial.print(kyneoIMU.baro.getTemperature(MS561101BA_OSR_4096), 1);    // Temperature reading
        Serial.print("\t");
        Serial.println(kyneoIMU.getBaroAlt(), 2);   //pres
      }
      
    }else if (cmd == 'b' || cmd == 'B'){
      while(!Serial.available()){
        Serial.print("Battery level (mV): ");
        Serial.println(util.battLevel());
        delay(500);
      }
      
    }else if (cmd == 'g' || cmd == 'G'){
      while(!Serial.available()){
        gnss_idx = gnss.getLatLon(lat, lon);
        if(gnss_idx == 2){
          gnss.getdate(day);
          gnss.gettime(hour);
          alt = gnss.getalt();
      
          Serial.println("--------------------");
          Serial.print("Fecha:    ");
          Serial.println(day);
          Serial.print("Hora:     ");
          Serial.println(hour);
          Serial.print("Latitud:  ");
          Serial.println(lat,6);
          Serial.print("Longitud: ");
          Serial.println(lon,6);
          Serial.print("Altitud:  ");
          Serial.println(alt);
        }else{
          Serial.println("No GNSS information read...");
        }
      }
      
    }else if (cmd == 'n' || cmd == 'N'){
      while(!Serial.available()){
        if(gnss.getSingleNMEA(frame, 100)!=0){
          Serial.print("NMEA received: ");
          Serial.print(frame);
        }else{
          Serial.println("No NMEA frame received");
        }
      }

    }else if (cmd == 'l' || cmd == 'L'){
      while(!Serial.available()){                                          // Blinks some cool morse code 😉
        Serial.println("Blinking LEDs!, (wair for whole message)");
        digitalWrite(GP_LED_0, HIGH);   // LED0 ON.
        delay(750/ledFactor);
        digitalWrite(GP_LED_0, LOW);    // LED0 OFF.
        delay(250/ledFactor);
        digitalWrite(GP_LED_0, HIGH);   // LED0 ON.
        delay(750/ledFactor);
        digitalWrite(GP_LED_0, LOW);    // LED0 OFF.
        delay(250/ledFactor);
        digitalWrite(GP_LED_0, HIGH);   // LED0 ON.
        delay(250/ledFactor);
        digitalWrite(GP_LED_0, LOW);    // LED0 OFF.
        delay(750/ledFactor);

        digitalWrite(GP_LED_0, HIGH);   // LED0 ON.
        delay(250/ledFactor);
        digitalWrite(GP_LED_0, LOW);    // LED0 OFF.
        delay(750/ledFactor);

        digitalWrite(GP_LED_0, HIGH);   // LED0 ON.
        delay(750/ledFactor);
        digitalWrite(GP_LED_0, LOW);    // LED0 OFF.
        delay(250/ledFactor);
        digitalWrite(GP_LED_0, HIGH);   // LED0 ON.
        delay(250/ledFactor);
        digitalWrite(GP_LED_0, LOW);    // LED0 OFF.
        delay(250/ledFactor);
        digitalWrite(GP_LED_0, HIGH);   // LED0 ON.
        delay(750/ledFactor);
        digitalWrite(GP_LED_0, LOW);    // LED0 OFF.
        delay(750/ledFactor);

        digitalWrite(GP_LED_0, HIGH);   // LED0 ON.
        delay(750/ledFactor);
        digitalWrite(GP_LED_0, LOW);    // LED0 OFF.
        delay(250/ledFactor);
        digitalWrite(GP_LED_0, HIGH);   // LED0 ON.
        delay(750/ledFactor);
        digitalWrite(GP_LED_0, LOW);    // LED0 OFF.
        delay(250/ledFactor);
        digitalWrite(GP_LED_0, HIGH);   // LED0 ON.
        delay(750/ledFactor);
        digitalWrite(GP_LED_0, LOW);    // LED0 OFF.
        delay(1500/ledFactor);

        
        digitalWrite(GP_LED_1, HIGH);   // LED0 ON.
        delay(750/ledFactor);
        digitalWrite(GP_LED_1, LOW);    // LED0 OFF.
        delay(250/ledFactor);
        digitalWrite(GP_LED_1, HIGH);   // LED0 ON.
        delay(250/ledFactor);
        digitalWrite(GP_LED_1, LOW);    // LED0 OFF.
        delay(750/ledFactor);

        digitalWrite(GP_LED_1, HIGH);   // LED0 ON.
        delay(250/ledFactor);
        digitalWrite(GP_LED_1, LOW);    // LED0 OFF.
        delay(250/ledFactor);
        digitalWrite(GP_LED_1, HIGH);   // LED0 ON.
        delay(750/ledFactor);
        digitalWrite(GP_LED_1, LOW);    // LED0 OFF.
        delay(750/ledFactor);

        digitalWrite(GP_LED_1, HIGH);   // LED0 ON.
        delay(250/ledFactor);
        digitalWrite(GP_LED_1, LOW);    // LED0 OFF.
        delay(250/ledFactor);
        digitalWrite(GP_LED_1, HIGH);   // LED0 ON.
        delay(250/ledFactor);
        digitalWrite(GP_LED_1, LOW);    // LED0 OFF.
        delay(250/ledFactor);
        digitalWrite(GP_LED_1, HIGH);   // LED0 ON.
        delay(250/ledFactor);
        digitalWrite(GP_LED_1, LOW);    // LED0 OFF.
        delay(250/ledFactor);
        digitalWrite(GP_LED_1, HIGH);   // LED0 ON.
        delay(750/ledFactor);
        digitalWrite(GP_LED_1, LOW);    // LED0 OFF.
        delay(750/ledFactor);

        digitalWrite(GP_LED_1, HIGH);   // LED0 ON.
        delay(250/ledFactor);
        digitalWrite(GP_LED_1, LOW);    // LED0 OFF.
        delay(250/ledFactor);
        digitalWrite(GP_LED_1, HIGH);   // LED0 ON.
        delay(250/ledFactor);
        digitalWrite(GP_LED_1, LOW);    // LED0 OFF.
        delay(250/ledFactor);
        digitalWrite(GP_LED_1, HIGH);   // LED0 ON.
        delay(250/ledFactor);
        digitalWrite(GP_LED_1, LOW);    // LED0 OFF.
        delay(750/ledFactor);

        digitalWrite(GP_LED_1, HIGH);   // LED0 ON.
        delay(250/ledFactor);
        digitalWrite(GP_LED_1, LOW);    // LED0 OFF.
        delay(250/ledFactor);
        digitalWrite(GP_LED_1, HIGH);   // LED0 ON.
        delay(750/ledFactor);
        digitalWrite(GP_LED_1, LOW);    // LED0 OFF.
        delay(750/ledFactor);

        digitalWrite(GP_LED_1, HIGH);   // LED0 ON.
        delay(750/ledFactor);
        digitalWrite(GP_LED_1, LOW);    // LED0 OFF.
        delay(2000/ledFactor);
      }
      
    }else{
      Serial.println("Command not found");
    }
    while(Serial.available()) a = Serial.read();                               // Serial buffer flushing
    options = 0;
  }
}

Link to software repository

More information about Kyneo go to Kyneo Manual

For all the latest software visit our GitHub page GEKO Navsat Github's examples

Kyneo Conectivity

Introduction

Connectivity is at the heart of the Kyneo development, that’s why in the following tutorial, we will explain how to use XBee and or Bluetooth hardware to create a communication network. It is important to note that even though the XBee has an specific bay inside the Kyneo’s board, Bluetooth devices are not standard, so each one should be adapted to the board. However they usually come with a serial connection, which can be plugged into the serial output of the Kyneo (Tx0, Rx0) ports.

Connectivity description

Even though Kyneo has many ports to communicate with sensors and other componentes, in this communication tutorial we will focus on serial communications.

There are two serial ports integrated in the Kyneo board. One is for the GNSS module and the other is shared between the XBee pinout and the USB, this is why, it is very important to remove the XBee in order to program the board, an error will occur otherwise. This Serial is what is going to carry the information to the XBee  or Bluetooth for transmission, this makes simple serial programming the easiest way to transmit data.

The following program is an example of this, this program will generate random numbers from 0 to 100 and print them on the Serial. This way, when connecting an XBee or Bluetooth to this port, the numbers will be sent to a paired device.

In order to visualise the numbers sent on the receiver side, we must connect the XBee to a visualisation tool such an FTDI adapter and plug that to a computer to open a Serial port (Can easily be done through Arduino), or we must connect our device to the Bluetooth net generated, any Bluetooth terminal app should show the received data.

Example Sketch Code – Simple Connectivity

/**************************************************************************************************
 * Kyneo XBee-BT connectivity Example
 * 
 * This sketch will send a random number from an xBee to another equaly configured one. It requires
 * two xBee modules, one conected to the kyneo and the other to the computer via an FTDI adapter. 
 * 
 * Instructions: Charge this program to the kyneo and unplug it. Connect one xBee to the kineo and
 * the other to the FTDI adapter. Plug the FTDI on the computer, open arduino, select the com port
 * and start the serial monitor. Start the kyneo, you should see the random numbers generated on 
 * the serial monitor.
 * 
 * 
 * Created by GEKO Navsat S.L. for Kyneo V2.0 board
 * 
 * This example is free software, given under a GPL3 license
 * 
 * KYNEO is a product designed by GEKO Navsat S.L. in Europe http://www.gekonavsat.com
 * For further information, visit: http://kyneo.eu/
 * 
 *************************************************************************************************/
#include <KyneoBoard.h>                                 //GekoNavsat libraries

int LoopRate = 500;  //milliseconds                     //Variable definition
float RandomNum;
int RandomMax = 100;

void setup() {
  Serial.begin(9600);                                   //Initialize the Serial port 
}  

void loop() {
  
  RandomNum = random(RandomMax);                        //Random number generator
  Serial.println(RandomNum);

  delay(LoopRate);
}  

Dual connectivity description

As we stated before, besides the GNSS module serial, we have only one serial port for the communications use. So, in order to have two communication devices, we will have to emulate one serial port using two of the digital output pins of the Kyneo. This is extremely easy to do using the function SoftwareSerial, we just have to state which pins we want to use as Tx and Rx.

In the following program, a new serial will be emulated and prime numbers from 0 to 6301 will be calculated and out posted from it, besides, non prime number will be sent through the standard serial.

Example Sketch Code – Dual Connectivity

/**************************************************************************************************
 * Kyneo Dual Connectivity Example
 * 
 * This sketch will send a random number from a bluetooth beacon conected to the kyneo. 
 * 
 * Instructions: In order to visualize the data sent, another bluetooth enabled device will be needed,
 * such a smartphone. Any bluetooth terminal should provide the tools to read the data.
 * 
 * 
 * PrimeSieve
 * Paul Badger 2009
 * Generates prime numbers less than 63,001 as shown
 * To extend just add more primes to prime table (and choose a larger data type)
 * The program will generate primes up to the square of the last prime table entry
 * 
 * 
 * Created by GEKO Navsat S.L. for Kyneo V2.0 board
 * 
 * This example is free software, given under a GPL3 license
 * 
 * KYNEO is a product designed by GEKO Navsat S.L. in Europe http://www.gekonavsat.com
 * For further information, visit: http://kyneo.eu/
 * 
 *************************************************************************************************/
#include <KyneoBoard.h>                                 // GekoNavsat libraries
#include <SoftwareSerial.h>                             // Other libraries

SoftwareSerial BTSerial(0, 1); // Rx, Tx                // Object definition

int LoopRate = 500;                                     // Variable definition
byte primes[]={ 
    2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101,
    103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 
    199, 211, 223, 227, 229, 233, 239, 241, 251};
const unsigned int TopPrimeIndex = sizeof(primes) - 1;      
const unsigned long TopPrimeSquared = (long)primes[TopPrimeIndex] * (long)primes[TopPrimeIndex];
int primeFlag;


void setup() {
  Serial.begin(9600);                                   // Initialize the Serial port 
  BTSerial.begin(9600);
  BTSerial.println("Setup done /n");
}  

void loop() {

  for (long x = 1; x < TopPrimeSquared; x++){
    for (long j=0; j < TopPrimeIndex; j++){
      
      primeFlag = true;
      if (x == primes[j]) break;
      if (x % primes[j] == 0){                          // If the test number from prime table
        primeFlag = false;                              // Then test number is not prime
        break;
      }
    }
    
    if (primeFlag == true){                             // Found a prime - print it
      BTSerial.println(x);
      delay(LoopRate);
    }else{
      Serial.println(x);
    }
  }
}  

Link to software repository

More information about Kyneo go to Kyneo Manual

For all the latest software visit our GitHub page GEKO Navsat Github's examples

Kyneo MARG Attitude Sensors

Introduction

IMU are the initials for Inertial Measuring Unit, this term is used to refer to of the accelerometers and gyroscopes, which, besides the magnetometer form the MARG (Magnetic, Angular Rate and Gravity) unit. This unit will provide an accurate reading on the boards spatial orientation.
In the following examples, we will show how to extract certain data from this units.

Raw data

Kyneo merges the measurements sent from the accelerometer-gyroscope, the magnetometer and the barometer to estimate the attitude with a high precision. In the following example, we will show how to obtain the values of the orientation sensors. The units of the data are stated bellow.

– The accelerometer columns values will oscillate between 1 and -1. If no sudden movements are made are made, the acceleration movements on each one of the axes will be related to the gravity. For example, when Kyneo is placed horizontally and with the GNSS module on the upper side, the acceleration value of the third column should be -1, since in that position Kyneo has its Z axis looking to earth.

– The gyroscope columns register the angular velocities on each one of the axes. In this way, making rapid turns forward and backward with respect to an axis we’ll be able to see the value changes only in one of the columns (or, at least, the bigger change will happen in one of them).

– The magnetometer columns show the magnetic field measurement on each one of the axes. If the device is placed on a low magnetic influence environment (far from big metallic objects and electronic devices), the only thing to be measured will be the Earth magnetic field, so aligning one of Kyneo’s axes with the North-South line we’ll obtain the maximum value of the magnetic field on that axis.

Example Sketch Code – IMU raw data

/**************************************************************************************************
 * Kyneo IMU raw data example
 * 
 * This sketch prints sensors data through serial. It uses FreeIMU library (by Fabio Varesano), 
 * which has been adapted to support Kyneo board.
 * Kyneo MARG includes:
 *  - A 3-axis accelerometer + gyroscope (MPU6050): 16-bit ADC converter, auxiliary and primary I2C
 *    interfaces,
 *  - A magnetometer (HMC5883): 12-bit ADC converter, I2C interface (connected to MPU6050), 
 *  - A barometric altimeter (MS561101BA): 24-bit ADC for temperature and pressure sensors, I2C
 *    interface.
 * 
 * 
 * Created by GEKO Navsat S.L. for Kyneo V2.0 board
 * 
 * This example is free software, given under a GPL3 license
 * 
 * KYNEO is a product designed by GEKO Navsat S.L. in Europe http://www.gekonavsat.com
 * For further information, visit: http://kyneo.eu/
 * 
 *************************************************************************************************/
#include <KyneoBoard.h>                                 //GekoNavsat libraries
#include <FreeIMU.h>

FreeIMU kyneoIMU;                                       //Object definition

int LoopRate = 10;  //milliseconds                      //Variable definition
int raw[11];
int counter = 9;


void setup()
{
  Serial.begin(9600);
  
  Wire.begin();
  kyneoIMU.init();
  
  Serial.println("Setup done\n");
}

void loop()
{
  counter++;
  if(counter == 10){                                    // Prints column titles every 10 lines
    counter = 0;  
    Serial.println("acc_x\tacc_y\tacc_z\tgyr_x\tgyr_y\tgyr_z\tmag_x\tmag_y\tmag_z\tTemp\tAlt");  
  }
                                                                            
  kyneoIMU.getRawValues(raw);                                               
  Serial.print((float)raw[0]/2048, 2);                                    // acc_x
  Serial.print("\t");
  Serial.print((float)raw[1]/2048, 2);                                    // acc_y
  Serial.print("\t");
  Serial.print((float)raw[2]/2048, 2);                                    // acc_z
  Serial.print("\t");
  Serial.print((float)raw[3]/16.384, 2);                                  // gyr_x
  Serial.print("\t");
  Serial.print((float)raw[4]/16.384, 2);                                  // gyr_y
  Serial.print("\t");
  Serial.print((float)raw[5]/16.384, 2);                                  // gyr_z
  Serial.print("\t");
  Serial.print((float)raw[6]/1.707, 1);                                   // mag_x
  Serial.print("\t");
  Serial.print((float)raw[7]/1.707, 1);                                   // mag_y
  Serial.print("\t");
  Serial.print((float)raw[8]/1.707, 1);                                   // mag_z
  Serial.print("\t");
  Serial.print(kyneoIMU.baro.getTemperature(MS561101BA_OSR_4096), 1);     // temp
  Serial.print("\t");
  Serial.println(kyneoIMU.getBaroAlt(), 2);                               // pres
  
  delay(LoopRate);
}
/*********************************************************************************
// Printed measures: 
//  accelerometer xyz - in g (1 g = 9.806 m/s2), dividing by +- 16g scale factor. 
//  gyroscope xyz - in deg/s, dividing by +-2000 deg/s scale factor. 
//  magnetometer xyz - in milliGauss, dividing by +-1.2 Gauss scale factor.
//  temperature (ºC) and altitude (m) 
**********************************************************************************/

Attitude

In aeronautics, we usually talk about the Euler angles: Roll, Pitch and Yaw. Representing these the orientation of the reference axis of the aircraft. The axis distribution on any airplane are as shown in the picture at the right, the axis on the Kyneo have to be taken assuming that the Kyneo’s nose points towards on the x direction (painted on the board).

Example Sketch Code – Euler angles

/**************************************************************************************************
 * Kyneo Euler angles Example
 * 
 * This sketch shows the euler angles of the board in the serial monitor or plotter
 * Created by GEKO Navsat S.L. for Kyneo V2.0 board
 * 
 * This example is free software, given under a GPL3 license
 * 
 * KYNEO is a product designed by GEKO Navsat S.L. in Europe http://www.gekonavsat.com
 * For further information, visit: http://kyneo.eu/
 * 
 *************************************************************************************************/
#include <KyneoBoard.h>                                 //GekoNavsat libraries
#include <FreeIMU.h>

FreeIMU kyneoIMU;                                       //Object definition

int LoopRate = 10;  //milliseconds                      //Variable definition
float att[3];
 

void setup() {
  
  Serial.begin(9600);
  
  Wire.begin();
  kyneoIMU.init();
  
  Serial.print("Setup done\n");
}

void loop() {
  kyneoIMU.getYawPitchRoll(att);                          // Sensor read

  Serial.print("Yaw: ");                                // Data Print
  Serial.print(att[0]);
  Serial.print(",");
  Serial.print(" Pitch: ");
  Serial.print(att[1]);
  Serial.print(",");
  Serial.print(" Roll: ");
  Serial.println(att[2]);
  
  delay(LoopRate);
}

Quaternions

The following example shows the obtention of the orientation Quaternions. Quaternions are an alternative way to represent the spatial orientation and rotation of an object. They are more compact, more stable and may be more efficient, thus their use is widely spread in a variety of applications.

 

Example Sketch Code – Quaternions

/**************************************************************************************************
 * Kyneo Quaternion example
 * 
 * This sketch prints the quaternions used to show for orientation and rotations of the system.
 * 
 * 
 * Created by GEKO Navsat S.L. for Kyneo V2.0 board
 * 
 * This example is free software, given under a GPL3 license
 * 
 * KYNEO is a product designed by GEKO Navsat S.L. in Europe http://www.gekonavsat.com
 * For further information, visit: http://kyneo.eu/
 * 
 *************************************************************************************************/
#include <KyneoBoard.h>                                 //GekoNavsat libraries
#include <FreeIMU.h>

FreeIMU kyneoIMU;                                       //Object definition

int LoopRate = 10;  //milliseconds                      //Variable definition
float q[4];
int counter = 9;


void setup() {
  
  Serial.begin(9600);
  
  Wire.begin();
  kyneoIMU.init();
  
  Serial.println("Setup done\n");
}

void loop() 
{ 
  counter++;
  if(counter == 10){                                                        // Prints column titles every 10 lines
    counter = 0;  
    Serial.println("q0\tq1\tq2\tq3"); 
  }

  kyneoIMU.getQ(q);
  Serial.print(q[0]);
  Serial.print("\t");
  Serial.print(q[1]);
  Serial.print("\t");
  Serial.print(q[2]);
  Serial.print("\t");
  Serial.println(q[3]);
  
  delay(LoopRate);
}

Link to software repository

More information about Kyneo go to Kyneo Manual
For all the latest software visit our GitHub page GEKO Navsat Github's examples

Kyneo GNSS positioning application

Introduction

GNSS stands for global navigation satellite system. GNSS units obtain their position from the estimated distances to the satellites in view. Once calculated, that information is transmitted to the system in a standard format known as NMEA frames (National Marine Electronics Association). There are some NMEA frames transmitting location information, but some others only include data concerning the number of satellites in view, the position of each of them and the whole constellation status, etc. So, in this tutorial, we will show how to obtain those NMEA frames as well as how to obtain the position and some additional info from them using Kyneo.

Simple GNSS program description

The GNSS module installed on Kyneo generated a máximum of 6 different NMEA frames. Three of them include position and/or altitude information, while the other three include data concerning Kyneo speed or information about the satellites on view. This means that a previous GNSS configuration has to be done in order to receive only those frames that are necessary for our particular application, as well as the frequency with which they are sent from the module to the micro controller.

 

Geko Navsat has developed a library specifically designed for all the GNSS uses so that is the first thing that must be included in our program. Right after that, we must declare our variables so that arduino knows how to deal with them.

In the setup process, the serials must be started, in this case, we must also start the serial port for the GNSS receiver and set it’s baud rate. Once that is complete, we will proceed to setup the NMEA frames as shown in the example program.

Finally, using the kyneo’s specific functions for day, hour, latitude, longitude and altitude, we can simply print the result of the calculation.

Example Sketch Code – GNSS Position

/**************************************************************************************************
 * Kyneo GNSS position Example.
 * 
 * This sketch will print the position measured by the kyneo board based in the GNSS sensor.
 * 
 * 
 * Created by GEKO Navsat S.L. for Kyneo V2.0 board
 * 
 * This example is free software, given under a GPL3 license
 * 
 * KYNEO is a product designed by GEKO Navsat S.L. in Europe http://www.gekonavsat.com
 * For further information, visit: http://kyneo.eu/
 * 
 *************************************************************************************************/
#include <KyneoBoard.h>                                 //GekoNavsat libraries
#include <KyneoGNSS.h>

KyneoGNSS gnss;                                         //Object definition

float lat, lon;                                         //Variable definition                                 
int alt;
char hour[10], day[10];

void setup()
{
  Serial.begin(9600);                                                              // Boards baudrate
  Serial1.begin(9600);                                                             // GNSS modules baudrate

  //set rate divider(GPGLL, GPRMC, GPVTG, GPGGA, GPGSA, GPGSV, PMTKCHN). 
  gnss.setNMEAOutput(    0,     1,     0,     1,     0,     0,       0);           //GPRMC 1Hz, GPGGA 1Hz
  
  Serial.println("Setup done\n");
}

void loop()
{
  if(gnss.getLatLon(lat, lon) == 2){                                               // Data adquisition
    gnss.getdate(day);
    gnss.gettime(hour);
    alt = gnss.getalt();
    
    Serial.println("--------------------");                                        //Data print
    Serial.print("Fecha:    ");
    Serial.println(day);
    Serial.print("Hora:     ");
    Serial.println(hour);
    Serial.print("Latitud:  ");
    Serial.println(lat,6);
    Serial.print("Longitud: ");
    Serial.println(lon,6);
    Serial.print("Altitud:  ");
    Serial.println(alt);
  }else{
    Serial.println("No GNSS information read...");
  }
}

GNSS bypass program description

Once understood how to get the NMEA frames out of a kyneo, lets see how we can configure the output of the GNSS sensor itself. For this, we need to make Kyneo read our instructions on the Serial monitor. So The following program will read from the computers serial and write on the GNSS chipsets one.

In order to configure the sensor, we will use the PMTK codes on the terminal. This will allow us to better control the output of the sensor and to play with features such as power saving modes or SBAS compatibility.

Example Sketch Code – GNSS Bypass

/**************************************************************************************************
 * Kyneo GNSS bypass Example.
 * 
 * This sketch will print the raw measured by the kyneo boards GNSS sensor, as well as accept PMTK 
 * commands for the GNSS sensor modification.
 * 
 * 
 * Created by GEKO Navsat S.L. for Kyneo V2.0 board
 * 
 * This example is free software, given under a GPL3 license
 * 
 * KYNEO is a product designed by GEKO Navsat S.L. in Europe http://www.gekonavsat.com
 * For further information, visit: http://kyneo.eu/
 * 
 *************************************************************************************************/
#include <KyneoBoard.h>                                                   //GekoNavsat libraries
#include <KyneoGNSS.h>

KyneoGNSS gnss;                                                           //Object definition

char c;                                                                   //Variable definition

void setup()
{
  Serial.begin(9600);                                                     // Boards baudrate
  Serial1.begin(9600);                                                    // GNSS modules baudrate

  //set rate divider(GPGLL, GPRMC, GPVTG, GPGGA, GPGSA, GPGSV, PMTKCHN). 
  gnss.setNMEAOutput(    0,     1,     5,     1,     0,     0,       0);  //GPRMC 1Hz, GPVTG 0.2Hz

  Serial.println("Setup done\n");
}

void loop()
{
  while(Serial1.available()){                                             //Serial1(GNSS) to Serial0 (USB)
    c = Serial1.read();
    Serial.print(c);
  }
  while(Serial.available()){                                              //Serial0 (USB) to Serial1(GNSS)
    c = Serial.read();
    Serial1.print(c);
  }
}

GNSS PPS program description

In order to make the correct calculations for the GNSS system to work, the clock time is crucial, that’s why GNSS satellites are equiped with atomic clocks, the most accurate ones on earth. For this reason, GNSS signal contents an extremely accurate time reading called PPS (Pulse Per Second), which can be used to have the exact time on the device, as well as to synchronise multiple devices.

In the following program we will use this extremely accurate data o blink and LED at the fixed rate of 1 second. In order to do this, we will read the GNSS_1PPS from our receiver and use that to make the LED blink.

Example Sketch Code – GNSS PPS

/**************************************************************************************************
 * Kyneo PPS signal Example.
 * 
 * This sketch will Light up a LED using the PPS signal obtained from the GNSS services
 * 
 * 
 * Created by GEKO Navsat S.L. for Kyneo V2.0 board
 * 
 * This example is free software, given under a GPL3 license
 * 
 * KYNEO is a product designed by GEKO Navsat S.L. in Europe http://www.gekonavsat.com
 * For further information, visit: http://kyneo.eu/
 * 
 *************************************************************************************************/
#include <KyneoBoard.h>                                 //GekoNavsat libraries

int LedTime = 100;  //milliseconds                      //Variable definition

void setup()
{
  Serial.begin(9600);                                   // Boards baudrate
  
  pinMode(GNSS_1PPS, INPUT);                            // Configure PPS signal pin as input
  pinMode(GP_LED_0, OUTPUT);                            // Configure LED0 as output
  
  Serial.println("Setup done\n");
}

void loop()
{
  if(digitalRead(GNSS_1PPS)){                           // Read PPS signal                      
    digitalWrite(GP_LED_0, HIGH);                      
    //Serial.println("LED ON!!");
    delay(LedTime);                              
    digitalWrite(GP_LED_0, LOW);
    //Serial.println("LED Off...");
  }
}

Link to software repository

Link to PMTK commands
Link to GNSS receiver datasheet
More information about Kyneo go to Kyneo Manual
For all the latest software visit our GitHub page GEKO Navsat Github's examples

First contact with Kyneo – Helloworld

Introduction

In the following tutorials, we will show how to get started programming the Kyneo board. Since Kyneo is based on Arduino, in order to fully take advantage of the following tutorials, we recommend to be familiarised with it. Besides, we highly encourage the user to read the Kyneo Starting Guide.

When we start working with a new Arduino device it is a good idea to make a first basic test to be sure that we are able to upload a sketch on the microcontroller and that with this code the device can execute some minimum basic actions.

First contact

Besides other advanced functionalities, Kyneo has two general purpose LEDs that can be programmed to help the user with some indications of what the chipset is doing.

The “Kyneo_HelloWorld” sample sketch shows how to turn both LEDs on in a sequence.

Example Sketch Code

/**************************************************************************************************
 * Kyneo Helloworld Example
 * 
 * This sketch prints messages throught terminal and blinks the boards LEDs
 * 
 * 
 * Created by GEKO Navsat S.L. for Kyneo V2.0 board
 * 
 * This example is free software, given under a GPL3 license
 * 
 * KYNEO is a product designed by GEKO Navsat S.L. in Europe http://www.gekonavsat.com
 * For further information, visit: http://kyneo.eu/
 * 
 *************************************************************************************************/
#include <KyneoBoard.h>                                 //GekoNavsat libraries
#include <KyneoUtils.h>


void setup() {
  
  Serial.begin(9600);                                 //Initialize the Serial port 
  
  Serial.println("Hello world");                      //Print message throught the Serial port
  pinMode(13, OUTPUT);                                //Set pin mode where the LED is
  delay(3000);                                        //Make the program wait
  Serial.println("Let's blink some LEDs");            //Print message throught the Serial port

  pinMode(GP_LED_0, OUTPUT);                         //LEDs digital output 0 set
  pinMode(GP_LED_1, OUTPUT);                         //LEDs digital output 1 set
}

void loop() {
 
  digitalWrite(GP_LED_0, HIGH);                         // set the LED on
  delay(500);
  digitalWrite(GP_LED_1, HIGH);                         // set the LED on
  delay(500);
  digitalWrite(GP_LED_0, LOW);                          // set the LED off
  delay(500);
  digitalWrite(GP_LED_1, LOW);                          // set the LED off
  delay(500);

}

Troubleshooting

Unable to connect to Kyneo:
- Make sure the drivers and libraries are properly installed
- Make sure the board is properly connected to the computer
- Make sure the board is ON

Unable to upload sketch to Kyneo
- Make sure that Kyneo is selected on the Arduino board menu
- Make sure to disconnect any other items connected to the board
- Make sure that the "Serial Selector" switch, located at the top of the board, next to the GNSS, is in the "M" position

Link to software repository

Getting started Kyneo Starting Guide
More information about Kyneo go to Kyneo Manual
For all the latest software visit our GitHub page GEKO Navsat Github's examples

Kyneo sends GNSS positioning over Bluetooth

Introduction

GNSS positioning sensors integrated in mobile phones do not have enough sensitivity to rely on them to carry out certain activities. Usually they can be trusted as a reference, but need to be supported by the information provided by other sources, such as the direct observation of the environment. This problem can be minimized by obtaining position information from a more sensitive GNSS device. Instead of changing the integrated GNSS antenna, we can make the mobile listen to the Bluetooth receiver. If a more consistent information arrives through this channel, our phone will be able to improve it's performance.

Sending frames from Kyneo to a mobile phone

Mobile phones have small global positioning antennas that present several problems due to lack of sensitivity, signal rebound errors or inability to connect to more than one satellite constellation. This means that in some circumstances we cannot trust faithfully on the location information that it is providing. In this post, we present Kyneo as a reliable GNSS information source to support our mobile device operation.

The useful application would be to draw the positioning data directly on a map showed on our mobile screen, but what we are going to do in this project is to see what is lying beneath that process.

Connecting to Kyneo

First of all, we need to download an app that allows us to see the raw data that our phone receives via Bluetooth. Android free app «Bluetooth Terminal» has been succesfully tested for this purpose.

Now we need to link the Kyneo unit to our mobile phone via Bluetooth. Once this is done, we will be able to read the GNSS frames sent by Kyneo. Depending on the preferences set in our Kyneo code, we will receive the corresponging data frames with the corresponding periodicity (GPGLL, GPRMC, GPVTG...).

Link to software repository

You can find all the necessary software to replicate this and many more things on our github repository at GEKO Navsat Github's repository

Kyneo MARG Unit Calibration Process

Introduction

Have you ever attempted to use an uncalibrated sensor before? If the answer is yes, then you have probably seen that the measurements from an uncalibrated sensor do not necessarily match reality. The calibration process allows us to match a measurement with the real value of the magnitude measured. Let's see how this affects us!

Uncalibrated readings and need of calibration

In an ideal world, the sensor will produce an output that will be linear within the whole measurement range. The accelerometer uses a 16-bit register to store the accelerometer measurements for each axis, i.e. they can have 65536 different integer values within the full-scale setting. Therefore, when configured to +-2g, a reading on any axis should be "0" when the acceleration on that axis is 0g and, "-32768" when the acceleration is -2g, and "+32767" then the acceleration is +2g.
If we now go to the real world, we will probably see that the output for 0g is a non-zero value. This difference is called the "offset error". Also, if we steadily increase the acceleration on an axis, we should get proportional increases in the readings of that axis, which hardly ever happens, indeed. The ratio between the real increase in the magnitude and the increase obtained in the readings is called the "gain error" or the "scale factor", meaning the slope of the linear response is not exactly "1". By multiplying a reading by its scale factor, we can obtain the real acceleration.
As you can see in the image on the right, the uncalibrated readings of the accelerometer and magnetometer make an ellipsoid instead of the expected sphere and, the projections of this ellipsoid over the different plans defined by the axis are ellipses. This is due to the fact that the axis scale factors are not the same for each axis (if the scale factors where the same, we would be seeing a sphere here). In the same way the offsets are not the same for the different axis, the center of the ellipses are not 0.
I hope by now you have realized how important calibration is.
So, let's start the calibration process!

Calibration process

To calibrate Kyneo, we must rotate it in several directions to obtain the ellipsoid we were talking about. The board should be turn around the accelerometer to avoid axial accelerations from the turns but, as long as you do it slow enough, there is no problem on rotating it around a point that is a bit off the accelerometer. An ellipsoid is defined by three ellipses. The algorithm used during calibration takes the maximum ellipses that result of the projections of the ellipsoid on the plans and calculates the scale factor for each axis in order to transform the ellipsoid into the expected sphere. In order to obtain these ellipses, we need to rotate the Kyneo as shown in the image below. The more turns we do, the better precision we will get as we increase the number of samples.

Results after calibration

In the image on the right you can see the spheres we were talking about. Through the calibration process, we get the different offsets and scale factors. By applying those to the data we obtained for the calibration, we transform the ellipsoids into spheres, showing the transformation that any measurements would go through by using the calculated parameters. As you can see in the images below, the calibration drastically improves the quality of the data. Note that we got the scale factors to obtaining axis-normalized data (the radius of the spheres is now 1). If you need to use different units, you'd have to make a later conversion.

Link to software repository

You can find all the necessary software to replicate this and many more things on our github repository at GEKO Navsat Github's repository