Last active
August 29, 2015 14:01
-
-
Save ibanezmatt13/7980f886f9304e1ff0c1 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <util/crc16.h> | |
#define RADIOPIN 9 | |
#define LED_1 5 | |
#define LED_2 6 | |
#define BAUD_RATE 50 // change this value to desired baud rate | |
int counter = 0; // sentence id | |
int tx_status = 0; | |
char *ptr = NULL; | |
char currentbyte; | |
int currentbitcount; | |
volatile boolean sentence_needed; | |
char send_datastring[102] = ""; | |
// this command will set flight 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 }; | |
ISR(TIMER1_COMPA_vect){ | |
switch (tx_status){ | |
case 0: // when the next byte needs to be gotten | |
if (ptr){ // if the pointer is valid | |
currentbyte = *ptr; | |
if (currentbyte){ // if there exists a byte at the pointer | |
tx_status = 1; | |
sentence_needed = false; | |
digitalWrite(LED_1, LOW); | |
} | |
else { | |
sentence_needed = true; | |
digitalWrite(LED_1, HIGH); | |
break; | |
} | |
} | |
else { | |
sentence_needed = true; | |
digitalWrite(LED_1, HIGH); | |
break; | |
} | |
case 1: // first bit about to be sent | |
rtty_txbit(0); // send start bit | |
tx_status = 2; | |
currentbitcount = 1; // set bit count to 0 ready for incrementing to 7 for last bit of a ASCII-7 byte | |
break; | |
case 2: // normal status, transmitting bits of byte (including first and last) | |
if (currentbyte & 1){ | |
rtty_txbit(1); | |
} | |
else { | |
rtty_txbit(0); | |
} | |
if (currentbitcount == 7){ // if we've just transmitted the final bit of the byte | |
tx_status = 3; | |
} | |
currentbyte = currentbyte >> 1; // shift all bits in byte 1 to right so next bit is LSB | |
currentbitcount++; | |
break; | |
case 3: // if all bits have been transmitted and we need to send the first of two stop bits | |
rtty_txbit(1); // send first stop bit | |
tx_status = 4; | |
break; | |
case 4: // ready to send the last of two stop bits | |
rtty_txbit(1); // send the final stop bit | |
ptr++; // increment the pointer for reading next byte in buffer | |
tx_status = 0; | |
break; | |
} | |
} | |
void rtty_txbit (int bit) | |
{ | |
digitalWrite(RADIOPIN, bit); | |
//digitalWrite(LED_2, bit); | |
//digitalWrite(LED_1, bit); | |
} | |
// function to convert latitude into decimal degrees | |
int check_latitude(char* latitude, char* ind, float* new_latitude) | |
{ | |
float result = 0; | |
if (strlen(ind)==1) | |
{ | |
result+=(latitude[0]-'0')*10; | |
result+=(latitude[1]-'0'); | |
result+=(strtod(&latitude[2],NULL) /60); | |
if (strcmp(ind,"S")==0) | |
{ | |
result*=-1; | |
} | |
*new_latitude = result; | |
return 1; | |
} | |
else | |
{ | |
return -1; | |
} | |
} | |
char *ltrim(char *string) { return(*string == ' ' ? string + 1 : string); } | |
// function to convert longitude into decimal degrees | |
int check_longitude(char* longitude, char* ind, float* new_longitude) | |
{ | |
float result = 0; | |
if (strlen(ind)==1) | |
{ | |
result+=(longitude[0]-'0')*100; | |
result+=(longitude[1]-'0')*10; | |
result+=(longitude[2]-'0'); | |
result+=(strtod(&longitude[3],NULL) /60); | |
if (strcmp(ind,"W")==0) | |
{ | |
result*=-1; | |
} | |
*new_longitude = result; | |
return 1; | |
} | |
else | |
{ | |
return -1; | |
} | |
} | |
// function to parse NMEA data and send to NTX2 | |
int parse_NMEA(char* mystring, int flightmode) | |
{ | |
float new_latitude; | |
float new_longitude; | |
char new_lat[12]; | |
char new_lon[12]; | |
char* trimmed_lon; | |
char time[12]; | |
int time_length; | |
char identifier[7]; | |
char latitude[12]; | |
char north_south[2]; | |
char longitude[12]; | |
char east_west[2]; | |
int lock; | |
int satellites; | |
char altitude[10]; | |
int check_latitude_error; | |
int check_longitude_error; | |
char* callsign = "$$$$VAYU"; | |
// split NMEA string into individual data fields and check that a certain number of values have been obtained | |
// $GPGGA,212748.000,5056.6505,N,00124.3531,W,2,07,1.8,102.1,M,47.6,M,0.8,0000*6B | |
if (sscanf(mystring, "%6[^,],%11[^,],%11[^,],%1[^,],%11[^,],%1[^,],%d,%d,%*[^,],%9[^,]", identifier, time, latitude, north_south, longitude, east_west, &lock, &satellites, altitude) == 9) | |
{ | |
// check for the identifer being invalid | |
if (strncmp(identifier, "$GPGGA", 6) != 0) | |
{ | |
return 0; | |
} | |
// check for a valid lock | |
if (lock == 0) | |
{ | |
digitalWrite(LED_2, HIGH); | |
return 0; | |
} | |
time_length = strlen(time); //get length of time | |
// check time is in the format: HHMMSS unless either already set or NMEA input was invalid | |
if (time_length > 6) | |
{ | |
sscanf(time, "%[^.]", time); | |
} | |
else if (time_length < 6) | |
{ | |
return 0; | |
} | |
// store the return value of the latitude/longitude functions accordingly | |
check_latitude_error = check_latitude(latitude, north_south, &new_latitude); | |
check_longitude_error = check_longitude(longitude, east_west, &new_longitude); | |
// if check_latitude() failed | |
if (check_latitude_error == -1) | |
{ | |
return 0; | |
} | |
// if check_longitude() failed | |
if (check_longitude_error == -1) | |
{ | |
return 0; | |
} | |
// convert floats to strings | |
dtostrf(new_latitude,9,6,new_lat); | |
dtostrf(new_longitude,9,6,new_lon); | |
trimmed_lon = ltrim(new_lon); | |
while (!sentence_needed){ | |
// wait until sentence needed | |
} | |
sprintf(send_datastring, "%s,%d,%s,%s,%s,%s,%d,%d,%d", callsign, counter ,time, new_lat, new_lon, altitude, lock, flightmode, satellites); | |
digitalWrite(LED_2, LOW); | |
unsigned int CHECKSUM = gps_CRC16_checksum(send_datastring); // Calculates the checksum for this datastring | |
char checksum_str[7]; | |
sprintf(checksum_str, "*%04X\n", CHECKSUM); | |
strcat(send_datastring,checksum_str); | |
counter++; | |
ptr = send_datastring; | |
} | |
else | |
{ | |
digitalWrite(LED_2, HIGH); | |
return 0; | |
} | |
} | |
void initialise_interrupt() | |
{ | |
// initialize Timer1 | |
cli(); // disable global interrupts | |
TCCR1A = 0; // set entire TCCR1A register to 0 | |
TCCR1B = 0; // same for TCCR1B | |
OCR1A = F_CPU / 1024 / (BAUD_RATE - 1); // set compare match register to desired timer count | |
TCCR1B |= (1 << WGM12); // turn on CTC mode: | |
// Set CS10 and CS12 bits for: | |
TCCR1B |= (1 << CS10); | |
TCCR1B |= (1 << CS12); | |
// enable timer compare interrupt: | |
TIMSK1 |= (1 << OCIE1A); | |
sei(); // enable global interrupts | |
} | |
void setupGPS() | |
{ | |
//disable unwanted NMEA sentences | |
Serial.println("$PUBX,40,GLL,0,0,0,0*5C"); | |
delay(1000); | |
Serial.println("$PUBX,40,GSA,0,0,0,0*4E"); | |
delay(1000); | |
Serial.println("$PUBX,40,RMC,0,0,0,0*47"); | |
delay(1000); | |
Serial.println("$PUBX,40,GSV,0,0,0,0*59"); | |
delay(1000); | |
Serial.println("$PUBX,40,VTG,0,0,0,0*5E"); | |
delay(1000); | |
} | |
// 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++) { | |
Serial.write(MSG[i]); | |
} | |
Serial.println(); | |
} | |
uint16_t gps_CRC16_checksum (char *string) | |
{ | |
size_t i; | |
uint16_t crc; | |
uint8_t c; | |
crc = 0xFFFF; | |
// Calculate checksum ignoring the first two $s | |
for (i = 4; i < strlen(string); i++) | |
{ | |
c = string[i]; | |
crc = _crc_xmodem_update (crc, c); | |
} | |
return crc; | |
} | |
int getUBX_ACK(uint8_t *MSG) { | |
uint8_t b; | |
uint8_t ackByteID = 0; | |
uint8_t ackPacket[10]; | |
unsigned long startTime = millis(); | |
// 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! | |
return 1; | |
} | |
// Timeout if no valid response in 3 seconds | |
if (millis() - startTime > 3000) { | |
return 0; | |
} | |
// Make sure data is available to read | |
if (Serial.available()) { | |
b = Serial.read(); | |
// Check that bytes arrive in sequence as per expected ACK packet | |
if (b == ackPacket[ackByteID]) { | |
ackByteID++; | |
} | |
else { | |
ackByteID = 0; // Reset and look again, invalid order | |
} | |
} | |
} | |
} | |
void setup() | |
{ | |
Serial.begin(9600); | |
pinMode(RADIOPIN, OUTPUT); | |
pinMode(LED_1, OUTPUT); | |
pinMode(LED_2, OUTPUT); | |
setupGPS(); | |
initialise_interrupt(); | |
} | |
void loop() | |
{ | |
static char datastring[100]; | |
char character; | |
static int n = 0; | |
int flightmode = 0; | |
if (Serial.available()){ | |
character = Serial.read(); | |
if (character == '$'){ | |
n = 0; | |
datastring[n] = character; | |
n = 1; | |
} | |
else if (character == '\n'){ | |
datastring[n] = '\0'; | |
flightmode = 0; | |
sendUBX(setNav, sizeof(setNav)/sizeof(uint8_t)); // set flight mode | |
flightmode = getUBX_ACK(setNav); // check flight mode enabled | |
parse_NMEA(datastring, flightmode); | |
n = 0; | |
flightmode = 0; | |
} | |
else if (n > 0){ | |
datastring[n] = character; | |
n++; | |
} | |
if (n >= 98){ | |
n = 0; | |
} | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment