Weasel code

From CUSF Wiki
Jump to navigation Jump to search

Introduction

This page contains the code archive for the Weasel Project. Only the revision of the code will remain on the Weasel Page.

Latest BETA Arduino Code

This is the latest code. It uses struct for gps, so gps data can be easily accessed for use in functions.

  1. The GPS nav mode check bug has been attempted to be fixed by re-ordering statements.
  2. The zeros after the decimal place bug has also been fixed, by converting the gps strings into char arrays in the GPS_Coord function.
  3. depend on NewSoftSerial library found here

As much as possible all bugs have been eliminated, but this code has not been tested on an Arduino with a fully functioning GPS. However it has been beta tested using test strings on a static arduino uno. Using this code is at your own risk, and I recommend some ground testing first. If you want to use some existing code, or want to see fragments. See our dedicated code dump page here.

// This code sets a FSA03 GPS module to give gps data to a radio module to downlink to earth. 

//Code written by Chris Murkin, Leo, Hannah, and CU Spaceflight with FSA03 set up code from the UKHAS website
//This code is written for the Cambridge University Space Flight project. 
//It was used for the Weasel tracker an introductory proect
//The check sum code was borrowed from CUSF Ferret Tracker, written by Jon Sowman avalible from Git hub. 

//This should work in all parts of the world, but be careful as gps.lat_int, and gps.lon_int are always positive

#include <NewSoftSerial.h>
#include <stdio.h>
#include <string.h>
#include <util/crc16.h>


//Set New soft serial to GPS
NewSoftSerial nss(3, 2);

//Declare Varibles ======================
char clock[8]; //The time
char lat[11], lon[11]; //Lat and Long, in separated parts
char no_sat[3], alt[8], a; //Various other readings
int alt_count= 0;
int gcount=0, comma_count=0, dot_count=0, array_count=0; //gcount is the number of chars from GPS per read
int ticks;
char buffer[200];
char str_out[200];
char callsign[7] = "WEASEL";

struct program_data {
  long clock_int; //This is the clock as an interger
  long hour; //Shouldn't need to be a long, but is multiplied a lot when used, so programs fails if this is an int.
  long minute;
  long second;
  int sat;
  long alt;
  long lat_int; //This is the latitude as an interger, in DD.MMMMMM before GPS_Coord, and DD.DDDDD after GPS_Coord NOTE always positive.
  long lon_int; //Longitude equvialant. 
  char ew; //This is either E or W depending on hemisphere
  char ns; //This is N or S depending on hemisphere
  char blanks[8]; //This is used to put in the blanks for output string, do not use for any data
  char lat[11]; //This will be a string, with the form of a signed float so should have -52.00123 for example after GPS_Coord is called
  char lon[11]; 
}
gps; //gps is the name of the varible with a structure of program_data. Refer to contents by gps.sat, gps.lat[5] etc...

void setup() {//Startup loop ====================================================

  // Start up serial ports
  nss.begin(38400);
  Serial.begin(115200); // used for debug ouput

  delay(2000); // Give the GPS time to come boot

  // Lower the baud rate to 9600 from 38.4k
  Serial.print("Setting uBlox port mode: ");
  uint8_t setPort[] = {
    0xB5, 0x62, 0x06, 0x00, 0x14, 0x00, 0x01, 0x00, 0x00, 0x00, 0xD0, 0x08, 0x00, 0x00, 0x80, 0x25, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9E, 0x95                };
  sendUBX(setPort, sizeof(setPort)/sizeof(uint8_t));

  // Switch baud rates on the software serial
  Serial.println("Switching to 9600b GPS serial");
  nss.begin(9600);
  delay(1000);

  set_GPS();

  ticks = 1; 


} //END OF SET UP LOOP


void loop()//===================================================================
{

  //READ THE GPS AND EXTRACT THE USEFUL DATA
  while (a != '*' && gcount <120 && nss.available()){
    a = nss.read(); //Reads the gps
    Serial.print(a); //Displays GPGGA (gps output string) on the USB debug

    if (a == ',') { //comma responce
      comma_count++;
      array_count=0;
    }
    if (a == '.') dot_count++; //dot count

    switch (comma_count) { //Switch command

    case 1: //This is the time
      //Serial.print(" Time ");
      if (dot_count ==0){ //Only work before the dot!
        clock[array_count]=a;
        if (a!= ',') array_count++;
      }
      break;

    case 2: // This is latitude
      if (a != '.'& a != ',') {
        lat[array_count] = a;
        array_count++;
      }
      break;

    case 3: //N/S
      if (a !=',') gps.ns=a;
      break;

    case 4: // This is latitude
      if (a != '.'& a != ',') {
        lon[array_count] = a;
        array_count++;
      }
      break;

    case 5: //E/W
      if (a !=',') gps.ew=a;
      break;

    case 7: //Stats
      if (a != '.'& a != ',') {
        no_sat[array_count] = a;
        array_count++;
      }
      break;


    case 9: //Alt

      if (a != '.'& a != ',') {
        alt[array_count] = a;
        array_count++;
        alt_count++;
      }
      break;
    }

    gcount++;
  } //This is the end of the loop that runs until a * is reached. 

  //Insert stop bits on the string. 
  clock[6]='\0';
  lat[10]='\0';
  lon[10]='\0';
  no_sat[2]='\0';
  alt[alt_count]='\0';


  //INT CONVERSION
  gps.lon_int = atol(lon);
  gps.lat_int = atol(lat);
  gps.alt = atol(alt)/10;
  gps.clock_int = atol(clock);
  gps.sat = atoi(no_sat);


  //TEST CODE = MAKESURE OFF FOR LAUNCH
  //gps.ew='W'; //This will simulate crossing the prime meridian
  //gps.ns='S'; //This will simulate crossing the equator. 

  GPS_coord(); //Convert GPS Coordinates
  time_split();  //Get time format ready for the radio
  print_debug();//This will output the data from the GPS via USB serial debugger. Can eliminate if required. 

  //Prepare the string for transmission
  int a = sprintf(buffer, "$$%s,%u,%.2ld:%.2ld:%.2ld,%s,%s,%ld,%.2u\0", callsign, ticks, gps.hour, gps.minute, gps.second, gps.lat, gps.lon, gps.alt, gps.sat);
  //int a = sprintf(buffer, "$$%s,%l
  unsigned int checksum = CRC16Sum(buffer);
  char checksum_str[6];
  sprintf(checksum_str, "*%04X\n", checksum);
  int b = sprintf(str_out, "%s%s\n\0", buffer, checksum_str);
  Serial.println(str_out);

  ticks++;

  // Iterate through the string transmitting byte-by-byte.
  int j=0;
  while(str_out[j] != 0) {
    rtty_50(str_out[j]); //Use 50 baud RTTY, to downlink using pin 5 as data pin
    j++;
  }

  delay(500); //This just adds in a delay before starting again with the next line of the code

  if (1.0*ticks/20 == ticks/20) set_GPS(); //This should every 20th cycle reset the GPS, ensuring that it remains in Nav Mode with all of the correct strings in place

  //RESET string decoder

  while(a != '$' && nss.available()) { //This runs through until a dollar sign is reached the start of a new part of the code. 
    a = nss.read();
  }

  comma_count = 0; //This initalise the varibles used in decoding a GPS string. 
  dot_count = 0;
  gcount = 0;
  alt_count = 0;


} //END OF MAIN LOOP


//FUNCTIONS  ================================================================================================

void rtty_50(char abit) { //This is the RTTY on pin 5 with 50 baud transmition string

  digitalWrite(5, LOW);    // START BIT set the radio to off
  delay(20);              // wait for 20ms

  for(int k = 0; k<8; k++){ //This is a loop as there are 8 bits in a byte, which is one charactor
    if (abit & 1){ // This will return 1 if the last bit is 1, or 0 if the last bit is 0, as 00000001 AND abcdefgh = 0000000h
      digitalWrite(5, HIGH);    //HIGH BIT set the radio to on
      delay(20);              // wait for 20ms
    }
    else{
      digitalWrite(5, LOW);    // Low BIT set the radio to off
      delay(20);              // wait for 20ms
    }
    abit = abit >>1; //Shift bits to the right by 1 step
  }

  digitalWrite(5, HIGH);    // END BIT set the radio to on
  delay(20);              // wait for 20ms

}//End of RTTY LOOP

void set_GPS(){
  // Set the navigation mode (Airborne, 1G)
  Serial.print("Setting uBlox nav mode: ");
  uint8_t setNav[] = {
    0xB5, 0x62, 0x06, 0x24, 0x24, 0x00, 0xFF, 0xFF, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10, 0x27, 0x00, 0x00, 0x05, 0x00, 0xFA, 0x00, 0xFA, 0x00, 0x64, 0x00, 0x2C, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0xDC};
  sendUBX(setNav, sizeof(setNav)/sizeof(uint8_t));
  getUBX_ACK(setNav);

  //Switch off GLL
  Serial.print("Switching off NMEA GLL: ");
  uint8_t setGLL[] = { 
    0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x2B};
  sendUBX(setGLL, sizeof(setGLL)/sizeof(uint8_t));
  getUBX_ACK(setGLL);

  //Switch off GSA
  Serial.print("Switching off NMEA GSA: ");
  uint8_t setGSA[] = { 
    0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x32};
  sendUBX(setGSA, sizeof(setGSA)/sizeof(uint8_t));
  getUBX_ACK(setGSA);

  // Switch off GSV
  Serial.print("Switching off NMEA GSV: ");
  uint8_t setGSV[] = { 
    0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x39};
  sendUBX(setGSV, sizeof(setGSV)/sizeof(uint8_t));
  getUBX_ACK(setGSV);

  // Switch off RMC
  Serial.print("Switching off NMEA RMC: ");
  uint8_t setRMC[] = { 
    0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x40};
  sendUBX(setRMC, sizeof(setRMC)/sizeof(uint8_t));
  getUBX_ACK(setRMC);

  //Switch off VTG
  Serial.print("Switching off NMEA VTG (Test): ");
  uint8_t setVTG[] = { 
    0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x05, 0x47};
  sendUBX(setVTG, sizeof(setVTG)/sizeof(uint8_t));
  getUBX_ACK(setVTG);
} //END OF SET GPS



void time_split(){ //This will split the clock interger to 3 components
  gps.hour = gps.clock_int/10000;
  gps.minute = (gps.clock_int - 10000*gps.hour)/100;
  gps.second = (gps.clock_int - 10000*gps.hour  -100*gps.minute);
}//End of time_split



void GPS_coord() {//This will convert the Coordinate to a form to transmit
  //Coordinate Conversion
  long lat_coord_dd, lon_coord_dd;
  long lat_coord_mm, lon_coord_mm;
  int a;

  lat_coord_dd= (gps.lat_int/10000000);
  lat_coord_mm = ((gps.lat_int - (10000000*lat_coord_dd))/60);
  gps.lat_int = lat_coord_dd*10000000 + 100*lat_coord_mm; //This should return a coordinate as an interger using DD.DDDDD notation
  
  lon_coord_dd= (gps.lon_int/10000000);
  lon_coord_mm = ((gps.lon_int - (10000000*lon_coord_dd))/60);
  gps.lon_int = lon_coord_dd*10000000 + 100*lon_coord_mm; //This should return a coordinate as an interger using DD.DDDDD notation

  //Create a char array in the form of a signed float
  //latitude
  zero_lookup(lat_coord_mm);
  if (gps.ns == 'N'){
    a = sprintf(gps.lat, "%.2ld.%s%ld\0", lat_coord_dd, gps.blanks, lat_coord_mm);
  }
  else{
    a = sprintf(gps.lat, "-%.2ld.%s%ld\0", lat_coord_dd, gps.blanks, lat_coord_mm); //This is negative when in the northern hemisphere
  }
  //longitude
  zero_lookup(lon_coord_mm);
  if (gps.ew == 'E'){
    a = sprintf(gps.lon, "%.2ld.%s%ld\0", lon_coord_dd, gps.blanks, lon_coord_mm);
  }
  else{
    a = sprintf(gps.lon, "-%.2ld.%s%ld\0", lon_coord_dd, gps.blanks, lon_coord_mm); //This is negative when in the southern hemisphere
  }
}//End of GPS shift


void zero_lookup(long small_part){ //This will fill in zeros after the decimal point 
  int n = 5;
  if (small_part == 0){
    n=4;
  }
  else{
    while (small_part != 0) {
      small_part = small_part/10;
      n--;
    }
  }
  if (n>0){
    for (int i = 0; i<n; i++){
      gps.blanks[i] = '0';
    }
  }
  gps.blanks[n] = '\0';

}//End of zero_lookup


void print_debug(){ //This will output the data from the GPS via USB serial debugger. 
  Serial.println(" ");
  Serial.print("Time: ");
  Serial.print(gps.hour);
  Serial.print(":");
  Serial.print(gps.minute);
  Serial.print(":");
  Serial.println(gps.second);
  Serial.print("Latitude: ");
  Serial.println(gps.lat);
  Serial.print("Longitude: ");
  Serial.println(gps.lon);
  Serial.print("Number of sats: ");
  Serial.println(gps.sat);
  Serial.print("Altitude: ");
  Serial.println(gps.alt); 
}//end of Debug


unsigned int CRC16Sum(char *string) { //This will find the check sum 
  unsigned int i;
  unsigned int crc;

  crc = 0xFFFF;

  // Calculate the sum, ignore $ sign's
  for (i = 0; i < strlen(string); i++) {
    if (string[i] != '$') crc = _crc_xmodem_update(crc,(uint8_t)string[i]);
  }

  return crc;
} //END OF CHECKSUM LOOP

// Send a byte array of UBX protocol to the GPS
void sendUBX(uint8_t *MSG, uint8_t len) {
  for(int i=0; i<len; i++) {
    nss.print(MSG[i], BYTE);
    Serial.print(MSG[i], HEX);
  }
  Serial.println();
}//END sendUBX


// Calculate expected UBX ACK packet and parse UBX response from GPS
boolean getUBX_ACK(uint8_t *MSG) {
  uint8_t b;
  uint8_t ackByteID = 0;
  uint8_t ackPacket[10];
  unsigned long startTime = millis();
  Serial.print(" * Reading ACK response: ");

  // Construct the expected ACK packet    
  ackPacket[0] = 0xB5;  // header
  ackPacket[1] = 0x62;  // header
  ackPacket[2] = 0x05;  // class
  ackPacket[3] = 0x01;  // id
  ackPacket[4] = 0x02;  // length
  ackPacket[5] = 0x00;
  ackPacket[6] = MSG[2];    // ACK class
  ackPacket[7] = MSG[3];    // ACK id
  ackPacket[8] = 0;     // CK_A
  ackPacket[9] = 0;     // CK_B

  // Calculate the checksums
  for (uint8_t i=2; i<8; i++) {
    ackPacket[8] = ackPacket[8] + ackPacket[i];
    ackPacket[9] = ackPacket[9] + ackPacket[8];
  }

  while (1) {

    // Test for success
    if (ackByteID > 9) {
      // All packets in order!
      Serial.println(" (SUCCESS!)");
      return true;
    }

    // Timeout if no valid response in 3 seconds
    if (millis() - startTime > 3000) { 
      Serial.println(" (FAILED!)");
      return false;
    }

    // Make sure data is available to read
    if (nss.available()) {
      b = nss.read();

      // Check that bytes arrive in sequence as per expected ACK packet
      if (b == ackPacket[ackByteID]) { 
        ackByteID++;
        Serial.print(b, HEX);
      } 
      else {
        ackByteID = 0;  // Reset and look again, invalid order
      }

    }
  }
}//End of getUBX_ACK

C++ GPGGA to Char Arrays

This program now will read a GPGGA string, and extract all info required. It needs to be intergrated in to the arduino code, and is close to working.

The program works by counting commas. Then needs converting into arduino code.

#include <stdio.h>
#include <iostream>
#include <stdlib.h>

using namespace std;

int main()
{

    //DECLARE Varibles
   char sentence []="$$GPGGA,183439.00,5211.88950,N,00007.22003,W,1,05,2.96,61.5,M,45.7,M,,*6B"; //Test String
   //char sentence []="$$GPGGA,183439.00,5211.88950,N,00007.22003,W,1,05(stat),2.96,61.5(alt),M,45.7,M,,*6B";

   char clock[8]; //The time
   char lat[11], lon[11]; //Lat and Long, in separated parts
   char ns, ew, no_sat[3], alt[8], a; //Various other readings
    int alt_count= 0;
   int gcount=0, comma_count=0, dot_count=0, array_count=0;
   long int lon_int, lat_int, alt_int, clock_int;
   int sat;

   for (int i=0; i<8; i++){
clock[i]=0;
}

while (sentence[gcount] != '*' & gcount <100){
a=sentence[gcount]; //Active charictar
cout << a ; //output a

if (a == ',') { //comma responce
        comma_count++;
        array_count=0;
        }
if (a == '.') dot_count++; //dot count
switch (comma_count) { //Switch command

  case 1: //This is the time
     if (dot_count ==0){ //Only work before the dot!
     clock[array_count]=a;
    //cout<< " time " << clock[array_count] << " count " << array_count;
     if (a!= ',') array_count++;
     }
     break;

  case 2: // This is latitude
     if (a != '.'& a != ',') {
     lat[array_count] = a;
     array_count++;
     }
     break;

case 3: //N/S
    if (a !=',') ns=a;
    break;

  case 4: // This is latitude
     if (a != '.'& a != ',') {
    lon[array_count] = a;
     array_count++;
     }
     break;

case 5: //E/W
    if (a !=',') ew=a;
    break;

case 7: //Stats
 if (a != '.'& a != ',') {
    no_sat[array_count] = a;
     array_count++;
     }
     break;


case 9: //Alt

 if (a != '.'& a != ',') {
    alt[array_count] = a;
     array_count++;
     alt_count++;
     }
    break;
  }

gcount++;
}
clock[6]='\0';
lat[10]='\0';
lon[10]='\0';
no_sat[2]='\0';
alt[alt_count-1]='\0';
cout<<endl;

//INT CONVERSION

lon_int = atol(lon);
lat_int = atol(lat);
alt_int = atol(alt);
clock_int = atol(clock);
sat = atoi(no_sat);

//Coordinate Conversion
int n_coor_dd, e_coor_dd;
       int n_coor_mm, e_coor_mm;
       long int n_out, e_out;
       n_coor_dd= (lat_int/10000000);
       cout << n_coor_dd<< endl;
       n_coor_mm = ((lat_int - (10000000*n_coor_dd))/60);
       n_out = n_coor_dd*10000000 + 100*n_coor_mm;
       cout << n_out<< endl;

      e_coor_dd= (lon_int/10000000);
       cout << e_coor_dd<< endl;
       e_coor_mm = ((lon_int - (10000000*e_coor_dd))/60);
       e_out = e_coor_dd*10000000 + 100*e_coor_mm;
       cout << e_out<< endl;

       if (ew == 'W')  e_out= -e_out;
       if (ns == 'S')  n_out = -n_out;

cout<<"Finished loop"<<endl;
cout<<"Time: " << clock <<endl;


//for (int i=0; i<6; i++){
//cout<<clock[i];
//}
//cout<<"end";
cout<<"Latitude: "<<lat<<ns<<endl;

cout<<"Longitude: "<<lon<<ew<<endl;
cout<<"Number of sats: "<<no_sat<<endl;
cout<<"Altitude: "<<alt<<endl;

//RESET
comma_count = 0;
dot_count =0;
gcount = 0;

}

GPS String Reader

This will read the string, and separate out the variables for GPS, time, and altitude. This will work find in a C++ complier. Sadly doesn't work when put on the Arduino. Any ideas?

#include <stdio.h>


int main()
{

    //Read the data_
   char sentence []="$GPGGA,183439.00,5211.88950,N,00007.22003,W,1,05,2.96,61.5,M,45.7,M,,*6B"; //Test String
   char N_S; //N/S
   char E_W; // E/W
   float n_coord, e_coord, altitude, GPStime;//this will be north/south coordinates

   sscanf (sentence, "$GPGGA,%f,%f,%c,%f,%c,%*d,%*d,%*f,%f,%*c,%*f,%*c,,*%*s", &GPStime, &n_coord, &N_S,&e_coord,&E_W,&altitude); //Analysis sentence
   printf ("%f %c %f %c %f %f\n",GPStime, N_S, n_coord,E_W,e_coord,altitude); //Will now display the answers


   //convert coordinates
        //convert North
       int n_coor_dd, e_coor_dd;
       float n_coor_mm, e_coor_mm;
       float n_out, e_out;
       n_coor_dd= (n_coord/100);
       n_coor_mm = ((n_coord - (100*n_coor_dd))/60);
       n_out = n_coor_dd + n_coor_mm;


       //covert East

       e_coor_dd= (e_coord/100);
       e_coor_mm = ((e_coord - (100*e_coor_dd))/60);
       e_out = e_coor_dd + e_coor_mm;


       //make sure degrees is negative if West of Meridan
       char W = 'W';
       char S = 'S';
       if (E_W == W)  e_out= -e_out;
       if (N_S == S)  n_out = -n_out;

       //Convert altitude to feet
       altitude = 3.2808399*altitude;


        printf ("%f\n", n_out);
        printf ("%f\n", e_out);
        printf ("%f\n", altitude);

  return 0; //End of program


}

cut the cord

when the tracker go beyond a certain distance away from the launch site. give out a signal using an amplifier circuit and an ematch ignites explosives to cut the balloon loose. the current parameters are 60km northeast 90 southeast 200 south west 250 northwest. variable "result" stores the result of the function: 0:in range 1: out of range but cord uncut 2:successfully cut off which can be sent by radio to notify the status of the balloon

circuit diagram

https://lh5.googleusercontent.com/-_okrrjXgOkE/TfvCmscXVxI/AAAAAAAAAVU/CRUWfjwJPWQ/s400/pyro.JPG

the transistor used 2N7000 but could use any enhance mode transistor.

int explode(int X, int Y);
void trigger();

int fire_pin=13;
int test_pin=12;                               //set pins change accordingly  

//put the following section in the setup.  
void setup() {            
  pinMode(fire_pin, OUTPUT);            // initialize the digital pin as an output.
  pinMode(test_pin, INPUT);
  digitalWrite(fire_pin, LOW);             //set it low so it doesn't pop when turned on.
}

//the following section in main loop
void loop() {
  float X=100;                       //latitude
  float Y=100;                      //equate to the longitude(remenber to divide by scale factor)
  int result;                          //store the result of the calls. 0:in range 1: out of range but cord uncut 2:successfully cut off
  if (explode(X,Y,result))trigger(result);
}

//function to check weather out of range. can run at and regualr interval. maybe everytime when the navimode resets
int explode(float X, float Y,int& result)
{
     X=X/360*2*3.1415926;
     Y=Y/360*2*3.1415926;
     result=1;
     float cam_x = 0.0017453;
     float cam_y = 0.45564;     
     float d = sqrt((X-cam_x)*(X-cam_x)+(X-cam_y)*(X-cam_y))*(6067);                      //perpenducular triangle
     if (X-cam_x>0 && Y-cam_y>0 && d>60) return (1);
     else if (X-cam_x>0 && Y-cam_y<0 && d>90) return(1);
     else if (X-cam_x<0 && Y-cam_y>0 && d>250) return(1);
     else if (X-cam_x<0 && Y-cam_y<0 && d>200) return(1);
     else {result=0; return (0);}
}

void trigger(int& result)
{
    for(int n=3;n>0;n++)//try trigger 3 times
    {
      digitalWrite(fire_pin, HIGH);    // set the pin hi
      delay(1000);                         //wait for a second not sure if its enough
      digitalWrite(fire_pin, LOW);    //set it back
    }
    if(!digitalRead(test_pin))result++;    //test weather successful
}

Nova 19 Arduino code

This is the code that was used in Nova 19. However this has 2 bugs:

  1. Every 20 ticks it resets the GPS into Nav mode. However this causes a blip in the output data string, causing the coordinates not to update, and the altitude to become zero
  2. If Coordinate is 0.08912, it will show as 0.89120. This is due to a bug cause by storing the part after the decimal point as a separate integer.

Both these bugs are corrected in the latest release at the top of this page, or on the project main page. This code is retained primary for historical purposes, or if people want a tested code.

// This code sets a FSA03 GPS module to give gps data to a radio module to downlink to earth. 

//Code written by Chris Murkin, Leo, Hannah, and CU Spaceflight with FSA03 set up code from the UKHAS website
//The check sum code was borrowed from CUSF Ferret Tracker, written by Jon Sowman avalible from Git hub. 

//This code will only work in the NORTHERN hemisphere, any people wishing to use this from southern hemisphere need to modify the sscanf function with a simlar 
//statement to the one currently used for putting the sign on EW transistion. 
#include <NewSoftSerial.h>
#include <stdio.h>
#include <string.h>
#include <util/crc16.h>


//Set New soft serial to GPS
NewSoftSerial nss(3, 2);

char clock[8]; //The time
char lat[11], lon[11]; //Lat and Long, in separated parts
char ns, ew, no_sat[3], alt[8], a; //Various other readings
int alt_count= 0;
int gcount=0, comma_count=0, dot_count=0, array_count=0;
long lon_int, lat_int;
long alt_int, clock_int, ticks;
int sat;
long  lat_big, lat_small, long_big, long_small;
long  hour, minute, second;


unsigned long chars;
unsigned short sentences, failed_checksum;
char buffer[200];
char str_out[200];
char callsign[7] = "WEASEL";

void setup() {
 
    // Start up serial ports
    nss.begin(38400);
    Serial.begin(115200); // used for debug ouput
 
    delay(2000); // Give the GPS time to come boot
 
    // Lower the baud rate to 9600 from 38.4k
    Serial.print("Setting uBlox port mode: ");
    uint8_t setPort[] = {0xB5, 0x62, 0x06, 0x00, 0x14, 0x00, 0x01, 0x00, 0x00, 0x00, 0xD0, 0x08, 0x00, 0x00, 0x80, 0x25, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9E, 0x95};
    sendUBX(setPort, sizeof(setPort)/sizeof(uint8_t));
 
    // Switch baud rates on the software serial
    Serial.println("Switching to 9600b GPS serial");
    nss.begin(9600);
    delay(1000);
 
    set_GPS();

        ticks = 1; 
               
         
} //END OF SET UP LOOP


void loop()
{
  
//READ THE GPS AND EXTRACT THE USEFUL DA
while (a != '*' && gcount <120 && nss.available()){
a = nss.read();
Serial.print(a);

if (a == ',') { //comma responce
        comma_count++;
        array_count=0;
        }
if (a == '.') dot_count++; //dot count

switch (comma_count) { //Switch command

  case 1: //This is the time
  //Serial.print(" Time ");
     if (dot_count ==0){ //Only work before the dot!
     clock[array_count]=a;
     if (a!= ',') array_count++;
     }
     break;

  case 2: // This is latitude
     if (a != '.'& a != ',') {
     lat[array_count] = a;
     array_count++;
     }
     break;

case 3: //N/S
    if (a !=',') ns=a;
    break;

  case 4: // This is latitude
     if (a != '.'& a != ',') {
    lon[array_count] = a;
     array_count++;
     }
     break;

case 5: //E/W
    if (a !=',') ew=a;
    break;

case 7: //Stats
 if (a != '.'& a != ',') {
    no_sat[array_count] = a;
     array_count++;
     }
     break;


case 9: //Alt

 if (a != '.'& a != ',') {
    alt[array_count] = a;
     array_count++;
     alt_count++;
     }
    break;
  }

gcount++;
} //This is the end of the loop that runs until a * is reached. 

//Insert stop bits on the string. 
clock[6]='\0';
lat[10]='\0';
lon[10]='\0';
no_sat[2]='\0';
alt[alt_count]='\0';


//INT CONVERSION

lon_int = atol(lon);
lat_int = atol(lat);
alt_int = atol(alt)/10;
clock_int = atol(clock);
sat = atoi(no_sat);


//TEST CODE = MAKESURE OFF FOR LAUNCH
//ew='W'; //This will simulate crossing the prime meridian

//GPS Coordinate preparation
GPS_Cood(lon_int, lat_int, lat_big, lat_small, long_big, long_small); //This will convert to DD.DDDDD format, and will split about decimal point for transmission.

      
//This will output the data from the GPS via USB serial debugger. 
Serial.println(" ");
Serial.print("Time: ");
Serial.println(clock_int);
Serial.print("Latitude: ");
Serial.print(lat_big);
Serial.print(".");
Serial.println(lat_small);
Serial.print("Longitude: ");
if (ew == 'W') Serial.print("-"); //This will put in the minus sign if West of prime meridian
Serial.print(long_big);
Serial.print(".");
Serial.println(long_small);
Serial.print("Number of sats: ");
Serial.println(sat);
Serial.print("Altitude: ");
Serial.println(alt_int);

//Get time format ready for the radio
time_split(clock_int, hour, minute, second);

    
//RESET string decoder

while(a != '$') { //This runs through until a dollar sign is reached the start of a new part of the code. 
a = nss.read();
}

comma_count = 0;
dot_count =0;
gcount = 0;
alt_count = 0;


//Prepare the string for transmission

         if (ew=='W'){ //Will put in the negative sign when in the Western Hemisphere. 
         int a = sprintf(buffer, "$$%s,%ld,%.2ld:%.2ld:%.2ld,%ld.%ld,-%ld.%ld,%ld,%.2u,\0", callsign, ticks, hour, minute, second, lat_big, lat_small, long_big, long_small, alt_int, sat);
         }
         else{
         int a = sprintf(buffer, "$$%s,%ld,%.2ld:%.2ld:%.2ld,%ld.%ld,%ld.%ld,%ld,%.2u,\0", callsign, ticks, hour, minute, second, lat_big, lat_small, long_big, long_small, alt_int, sat);
         }
          unsigned int checksum = CRC16Sum(buffer);
         char checksum_str[6];
         sprintf(checksum_str, "*%04X\n", checksum);
         int b = sprintf(str_out, "%s%s\n\0", buffer, checksum_str);
         Serial.println(str_out);
         
         ticks++;
         //buffer="$$WEASEL,62,17:51:5249,52.19846,0.12082,0";
                
          // Iterate through the string transmitting byte-by-byte.
        int j=0;
        while(str_out[j] != 0) {
           rtty_50(str_out[j]); //Use 50 baud RTTY, to downlink using pin 5 as data pin
           j++;
    }
    
    delay(500); //This just adds in a delay before starting again with the next line of the code
    
    if (1.0*ticks/10 == ticks/10) set_GPS(); //This should every tenth cycle reset the GPS, ensuring that it remains in Nav Mode with all of the correct strings in place
     
} //END OF MAIN LOOP
  
  
 //FUNCTIONS  
  
  void rtty_50(char abit) { //This is the RTTY on pin 5 with 50 baud transmition string
    
     digitalWrite(5, LOW);    // START BIT set the radio to off
          delay(20);              // wait for 20ms
          
          for(int k = 0; k<8; k++){ //This is a loop as there are 8 bits in a byte, which is one charactor
            if (abit & 1){ // This will return 1 if the last bit is 1, or 0 if the last bit is 0, as 00000001 AND abcdefgh = 0000000h
              digitalWrite(5, HIGH);    //HIGH BIT set the radio to on
              delay(20);              // wait for 20ms
            }
            else{
              digitalWrite(5, LOW);    // Low BIT set the radio to off
              delay(20);              // wait for 20ms
            }
            abit = abit >>1; //Shift bits to the right by 1 step
          }
          
          digitalWrite(5, HIGH);    // END BIT set the radio to on
          delay(20);              // wait for 20ms
    
  }//End of RTTY LOOP
  
  void set_GPS(){
    // Set the navigation mode (Airborne, 1G)
    Serial.print("Setting uBlox nav mode: ");
    uint8_t setNav[] = {0xB5, 0x62, 0x06, 0x24, 0x24, 0x00, 0xFF, 0xFF, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10, 0x27, 0x00, 0x00, 0x05, 0x00, 0xFA, 0x00, 0xFA, 0x00, 0x64, 0x00, 0x2C, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0xDC};
    sendUBX(setNav, sizeof(setNav)/sizeof(uint8_t));
    getUBX_ACK(setNav);
 
    //Switch off GLL
    Serial.print("Switching off NMEA GLL: ");
    uint8_t setGLL[] = { 0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x2B };
    sendUBX(setGLL, sizeof(setGLL)/sizeof(uint8_t));
    getUBX_ACK(setGLL);
 
     //Switch off GSA
     Serial.print("Switching off NMEA GSA: ");
    uint8_t setGSA[] = { 0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x32 };
    sendUBX(setGSA, sizeof(setGSA)/sizeof(uint8_t));
    getUBX_ACK(setGSA);
 
    // Switch off GSV
    Serial.print("Switching off NMEA GSV: ");
    uint8_t setGSV[] = { 0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x39 };
    sendUBX(setGSV, sizeof(setGSV)/sizeof(uint8_t));
    getUBX_ACK(setGSV);
 
    // Switch off RMC
    Serial.print("Switching off NMEA RMC: ");
    uint8_t setRMC[] = { 0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x40 };
    sendUBX(setRMC, sizeof(setRMC)/sizeof(uint8_t));
    getUBX_ACK(setRMC);

        //Switch off VTG
        Serial.print("Switching off NMEA VTG (Test): ");
        uint8_t setVTG[] = { 0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x05, 0x47 };
    sendUBX(setVTG, sizeof(setVTG)/sizeof(uint8_t));
    getUBX_ACK(setVTG);
  } //END OF SET GPS
  
  
  
  void time_split(long clock_int, long  &hour, long  &minute, long &second){ //This will split the clock interger to 3 components
hour = clock_int/10000;
minute = (clock_int - 10000*hour)/100;
second = (clock_int - 10000*hour  -100*minute);
  }//End of time_split
  
  
  
 void GPS_Cood(long lon_int, long lat_int, long &lat_big, long &lat_small, long &long_big, long &long_small) {//This will convert the Coordinate to a form to transmit
    //Coordinate Conversion
long n_coor_dd, e_coor_dd;
long n_coor_mm, e_coor_mm;
long n_out, e_out;
n_coor_dd= (lon_int/10000000);
n_coor_mm = ((lon_int - (10000000*n_coor_dd))/60);
n_out = n_coor_dd*10000000 + 100*n_coor_mm;


e_coor_dd= (lat_int/10000000);
e_coor_mm = ((lat_int - (10000000*e_coor_dd))/60);
e_out = e_coor_dd*10000000 + 100*e_coor_mm;
       
//Get gps cooridinates ready for transmission
lat_big = e_out/10000000;
lat_small = e_out - 10000000*lat_big;
long_big = n_out/10000000;
long_small = n_out-10000000*long_big;
    
}//End of GPS shift


  
  unsigned int CRC16Sum(char *string) { //This will find the check sum 
    unsigned int i;
    unsigned int crc;

    crc = 0xFFFF;

    // Calculate the sum, ignore $ sign's
    for (i = 0; i < strlen(string); i++) {
        if (string[i] != '$') crc = _crc_xmodem_update(crc,(uint8_t)string[i]);
    }

    return crc;
    } //END OF CHECKSUM LOOP

 // Send a byte array of UBX protocol to the GPS
void sendUBX(uint8_t *MSG, uint8_t len) {
    for(int i=0; i<len; i++) {
        nss.print(MSG[i], BYTE);
        Serial.print(MSG[i], HEX);
    }
    Serial.println();
}
 
 
// Calculate expected UBX ACK packet and parse UBX response from GPS
boolean getUBX_ACK(uint8_t *MSG) {
    uint8_t b;
    uint8_t ackByteID = 0;
    uint8_t ackPacket[10];
    unsigned long startTime = millis();
    Serial.print(" * Reading ACK response: ");
 
    // Construct the expected ACK packet    
    ackPacket[0] = 0xB5;    // header
    ackPacket[1] = 0x62;    // header
    ackPacket[2] = 0x05;    // class
    ackPacket[3] = 0x01;    // id
    ackPacket[4] = 0x02;    // length
    ackPacket[5] = 0x00;
    ackPacket[6] = MSG[2];  // ACK class
    ackPacket[7] = MSG[3];  // ACK id
    ackPacket[8] = 0;       // CK_A
    ackPacket[9] = 0;       // CK_B
 
    // Calculate the checksums
    for (uint8_t i=2; i<8; i++) {
        ackPacket[8] = ackPacket[8] + ackPacket[i];
        ackPacket[9] = ackPacket[9] + ackPacket[8];
    }
 
    while (1) {
 
        // Test for success
        if (ackByteID > 9) {
                // All packets in order!
                Serial.println(" (SUCCESS!)");
                return true;
        }
 
        // Timeout if no valid response in 3 seconds
        if (millis() - startTime > 3000) { 
            Serial.println(" (FAILED!)");
            return false;
        }
 
        // Make sure data is available to read
        if (nss.available()) {
            b = nss.read();
 
            // Check that bytes arrive in sequence as per expected ACK packet
            if (b == ackPacket[ackByteID]) { 
                ackByteID++;
                Serial.print(b, HEX);
            } else {
                ackByteID = 0;  // Reset and look again, invalid order
            }
 
        }
    }
}