#include "kiss.h" /* * KISS encoder uses two steps: * * Step 1: encodes the LoRa APRS frame into an AX.25 frame. DONE * Step 2: encapsulates the AX.25 frame into a KISS frame. DONE * * struct aprs_frame { * uint8_t source_address[10]; * uint8_t digi_path[255]; * uint8_t data_field[255]; * uint8_t message[255]; * uint8_t digis[10][10]; * uint8_t acknowledge_number[255]; * bool acknowledge_request = false; * uint16_t server_command = 0; * uint16_t number_of_digipeaters = 0; * * uint8_t valid_apsr_data = false; * }; * */ uint16_t KissClass::EncodeFrame(struct aprs_frame *aprsframe, struct ax25_frame *ax25frame) { uint8_t *encoded_call; uint16_t cnt=0; uint16_t position = 0; uint8_t digi_cnt = 0; uint8_t escaped_string_cnt=0; //printf("Encode KISS"); // Destination call //printf("Destination: "); encoded_call = EncodeCall(aprsframe->digis[0]); cnt = 0; while (cnt < 7) { ax25frame->complete[position] = *(encoded_call+cnt); //printf("0x%X ", *(encoded_call+cnt)); //printf("0x%X ", ax25frame->complete[position]); cnt++; position++; } //printf("\n"); // Source call //printf("Source: "); cnt=0; encoded_call = EncodeCall(aprsframe->source_address); while (cnt < 7) { ax25frame->complete[position] = *(encoded_call+cnt); //printf("0x%X ", *(encoded_call+cnt)); //printf("0x%X ", ax25frame->complete[position]); cnt++; position++; } //printf("\n"); // No digipeaters in path, so destination is the last call and therefore the 'last' flag should be set if (aprsframe->number_of_digipeaters == 0) { ax25frame->complete[position-1] |= 0x01; } else { // Path (digipeaters) //printf("Path: "); cnt=0; digi_cnt = 1; // Start at position 1 as position 0 contains destination (which we already encoded) while (aprsframe->number_of_digipeaters-- != 0) { encoded_call = EncodeCall(aprsframe->digis[digi_cnt]); while (cnt < 7) { ax25frame->complete[position] = *(encoded_call+cnt); //printf("0x%X ", *(encoded_call+cnt)); //printf("0x%X ", ax25frame->complete[position]); cnt++; position++; } digi_cnt++; cnt = 0; } //printf("\n"); // Set 'last' flag ax25frame->complete[position-1] |= 0x01; } // Add control fields ax25frame->complete[position++] = 0x03; ax25frame->complete[position++] = 0xF0; // All the dificult bits are done, now we just add the payload. //printf("Data:"); cnt = 0; while (aprsframe->data_field[cnt] != 0 && cnt < 256) { ax25frame->complete[position] = aprsframe->data_field[cnt]; //printf("0x%X ", ax25frame->complete[position]); cnt++; position++; } //printf( "\n"); // Store length of AX25 frame ax25frame->lenght = position; // Encapsulate AX.25 frame in KISS frame (including escaping FEND codes) putchar(FEND); putchar(CMD_DATA); escaped_string_cnt=0; ax25frame->encoded_kiss_frame[escaped_string_cnt++] = FEND; ax25frame->encoded_kiss_frame[escaped_string_cnt++] = CMD_DATA; cnt=0; position=ax25frame->lenght; while (position-- != 0) { // Escape FESC and TFEND if (ax25frame->complete[cnt] == FEND) { putchar(FESC); putchar(TFEND); ax25frame->encoded_kiss_frame[escaped_string_cnt++] = FESC; ax25frame->encoded_kiss_frame[escaped_string_cnt++] = TFEND; } else if (ax25frame->complete[cnt] == FESC) { putchar(FESC); putchar(TFESC); ax25frame->encoded_kiss_frame[escaped_string_cnt++] = FESC; ax25frame->encoded_kiss_frame[escaped_string_cnt++] = TFESC; } else { putchar(ax25frame->complete[cnt]); ax25frame->encoded_kiss_frame[escaped_string_cnt++] = ax25frame->complete[cnt]; } cnt++; } putchar(FEND); ax25frame->encoded_kiss_frame[escaped_string_cnt++] = FEND; //printf("\n"); return 0; } /* * Decodes a KISS frame from the usb serial port * * Input : string starting with FEND and ending with FEND * Output: filled struct AprsFrame * Return: 0 = OK, 1 = ERROR, 2 = EXIT KISS MODE */ uint16_t KissClass::DecodeFrame(uint8_t string[], struct kiss_tx_frame *kisstxframe) { uint16_t position =0 ; uint16_t loop_counter = 0; uint16_t cnt =0 ; uint16_t ssid = 0; uint16_t last_digi = 0; uint16_t digi_cnt = 0; //printf("Decode KISS"); kisstxframe->valid_data = false; // Not a valid frame if (string[position] != FEND) return 1; position++; // Is data frame: we de-escape the string and put it back in the same string with a NULL at the end as terminator. if (string[position] == CMD_DATA) { position++; while (string[position] != FEND) { // De-escape codes if (string[position] == FESC && string[position+1] == TFEND) { string[cnt] = FEND; position++; } else if (string[position] == FESC && string[position+1] == TFESC) { string[cnt] = FESC; position++; } else { string[cnt] = string[position]; } cnt++; position++; } string[cnt] = 0; //Terminate string } // Is command to exit KISS MODE else if (string[position] == CMD_EXIT_KISS) { return 2; } // If we are here we have a valid AX.25 frame in 'string' which is decapsulated from its KISS frame /* 0-5 destination 6 destination ssid 7-12 source 13 desitination sidd vanaf byte 13 LAST FLAG zoeken 03 f0 payload tot aan NULL */ // Decode destination call position = 0; cnt = 0; loop_counter = 0; while (loop_counter < 6) { kisstxframe->digis[0][cnt] = string[position] >> 1; // Remove space if (kisstxframe->digis[0][cnt] == ' ') cnt--; position++; loop_counter++; cnt++; //printf("%u ", position); } // Extract SSID //printf("position: %u\n", position); ssid = (string[position] & 0b00011110) >> 1; //printf("DIGI SSID: %u (%u)\n", ssid, position); // Place SSID behind call or not if it is zero if (ssid != 0) { kisstxframe->digis[0][cnt++] = '-'; if (ssid < 10) { kisstxframe->digis[0][cnt++] = ssid + 48; } else { kisstxframe->digis[0][cnt++] = '1'; kisstxframe->digis[0][cnt++] = (ssid%10)+48; } } // Is has-been-repeated flag set? if(string[position] & 0b10000000) { kisstxframe->digis[0][cnt] = '*'; cnt++; } kisstxframe->digis[0][cnt] = 0; //terminate string position++; // Decode source call cnt = 0; loop_counter=0; while (loop_counter < 6) { kisstxframe->source_address[cnt] = string[position] >> 1; // Remove space if (kisstxframe->source_address[cnt] == ' ') cnt--; position++; loop_counter++; cnt++; //printf("%u ", position); } // Extract SSID ssid = (string[position] & 0b00011110) >> 1; //printf("sources SSID: %u (%u)\n", ssid, position); // Place SSID behind call or not if it is zero if (ssid != 0) { kisstxframe->source_address[cnt++] = '-'; if (ssid < 10) { kisstxframe->source_address[cnt++] = ssid + 48; } else { kisstxframe->source_address[cnt++] = '1'; kisstxframe->source_address[cnt++] = (ssid%10)+48; } } // Is has-been-repeated flag set? if(string[position] & 0b10000000) { kisstxframe->source_address[cnt] = '*'; cnt++; } kisstxframe->source_address[cnt] = 0; //terminate string // Check LAST flag if ( (string[position] & 0b00000001)) last_digi = 1; position++; // Decode digi path digi_cnt=1; while (last_digi == 0) { cnt = 0; loop_counter=0; while (loop_counter < 6) { kisstxframe->digis[digi_cnt][cnt] = string[position] >> 1; // Remove space if (kisstxframe->digis[digi_cnt][cnt] == ' ') cnt--; position++; loop_counter++; cnt++; //printf("%u ", position); } // Extract SSID ssid = (string[position] & 0b00011110) >> 1; //printf("DIGI SSID: %u (%u)\n", ssid, position); // Place SSID behind call or not if it is zero if (ssid != 0) { kisstxframe->digis[digi_cnt][cnt++] = '-'; if (ssid < 10) { kisstxframe->digis[digi_cnt][cnt++] = ssid + 48; } else { kisstxframe->digis[digi_cnt][cnt++] = '1'; kisstxframe->digis[digi_cnt][cnt++] = (ssid%10) + 48; } } // Is has-been-repeated flag set? if(string[position] & 0b10000000) { kisstxframe->digis[digi_cnt][cnt] = '*'; cnt++; } kisstxframe->digis[digi_cnt][cnt] = 0; //terminate string // Check LAST flag (also ends when more than 10 digis) if ( (string[position] & 0b00000001) || digi_cnt > 8) last_digi = 1; digi_cnt++; position++; } kisstxframe->number_of_digipeaters = digi_cnt-1; //printf("Digipeaters: %u \n",kisstxframe->number_of_digipeaters ); //Skip the two control fields position++; position++; //Rest of string up to NULL is payload cnt = 0; kisstxframe->data_field[cnt++] = ':'; while (string[position] != 0 && position < 512) { kisstxframe->data_field[cnt] = string[position]; position++; cnt++; } kisstxframe->data_field[cnt] = 0; //terminate string kisstxframe->valid_data = true; return 0; } /* * Encodes call according to AX.25 specs. * * input : string with call and ssid as readable characters * output: pointer to memory location (7 bytes with the encoded call) * */ uint8_t * KissClass::EncodeCall(uint8_t string[]) { uint8_t position = 0; uint8_t cnt = 0; uint8_t repeat_flag = 0; static uint8_t call[7] = { 0,0,0,0,0,0,0} ; uint8_t ssid = 0; // if asterix is pressent in string, than the message has been repeated, so set the 'has been repeated' flag in the ssid register // At the same time delete the asterix and place a NULL (string terminator) at its place. As the asterix should be the last character in the string, this should work. position = 0; while( string[position] != 0) { if (string[position] == '*') { repeat_flag = 1; string[position] == 0; break; } position++; } position = 0; // extract call while( string[position] != 0 || cnt < 6) { if ( string[position] == '-') { position++; break; } else { call[cnt] = string[position] << 1; } cnt++; position++; } // pad with spaces to a length of 6 while( cnt < 6 ) { call[cnt++] = ' ' << 1; } // extract ssid cnt=0; while( string[position] != 0 ) { if (string[position] >= 48 && string[position] <= 57) { ssid = 10*ssid + string[position]-48; position++; } } ssid = ssid << 1; ssid |= 0b01100000; if (repeat_flag == 1) ssid |= 0b10000000; // add encoded ssid to encoded call array call[6] = ssid; /* //printf("SSID: 0x%X\n", ssid); cnt = 0; while (cnt < 7) { //printf("0x%X ", call[cnt++]); } //printf("\n"); */ return call; }