diff --git a/include/dsd.h b/include/dsd.h index 9cd0a56..689dee9 100644 --- a/include/dsd.h +++ b/include/dsd.h @@ -1409,6 +1409,7 @@ void harris_lptt (dsd_opts * opts, dsd_state * state, uint8_t * input, uint32_t void nxdn_gps_report(dsd_opts * opts, dsd_state * state, uint8_t * input, uint32_t src); void utf16_to_text (dsd_state * state, uint8_t wr, uint16_t len, uint8_t * input); void utf8_to_text (dsd_state * state, uint8_t wr, uint16_t len, uint8_t * input); +uint8_t nmea_sentence_checker(dsd_opts * opts, dsd_state * state, uint8_t * input, uint8_t slot, int len); //"DMR STEREO" void dmrBSBootstrap (dsd_opts * opts, dsd_state * state); diff --git a/src/dmr_pdu.c b/src/dmr_pdu.c index b1ee914..3973f3a 100644 --- a/src/dmr_pdu.c +++ b/src/dmr_pdu.c @@ -995,8 +995,9 @@ void dmr_locn (dsd_opts * opts, dsd_state * state, uint16_t len, uint8_t * DMR_P //write to LRRP file if (opts->lrrp_file_output == 1) { - char * timestr = getTimeC(); + char * datestr = getDateS(); + char * timestr = getTimeC(); //open file by name that is supplied in the ncurses terminal, or cli FILE * pFile; //file pointer diff --git a/src/dsd_frame_sync.c b/src/dsd_frame_sync.c index 426eae2..1fec52a 100644 --- a/src/dsd_frame_sync.c +++ b/src/dsd_frame_sync.c @@ -1212,8 +1212,11 @@ getFrameSync (dsd_opts * opts, dsd_state * state) { strncpy (synctest10, (synctest_p - 9), 10); //FSW only if ( - (strcmp (synctest10, "3131331131") == 0 ) //this seems to be the most common 'correct' pattern on Type-C - || (strcmp (synctest10, "3331331131") == 0 ) //this seems to be the most common 'correct' pattern on Conventional + (strcmp (synctest10, "3131331131") == 0 ) + || (strcmp (synctest10, "3331331131") == 0 ) + || (strcmp (synctest10, "3131331111") == 0 ) + // || (strcmp (synctest10, "3331331111") == 0 ) + // || (strcmp (synctest10, "3131311131") == 0 ) ) { @@ -1239,8 +1242,11 @@ getFrameSync (dsd_opts * opts, dsd_state * state) { strncpy (synctest10, (synctest_p - 9), 10); //FSW only if ( - (strcmp (synctest10, "1313113313") == 0 ) //this seems to be the most common 'correct' pattern on Type-C - || (strcmp (synctest10, "1113113313") == 0 ) //this seems to be the most common 'correct' pattern on Conventional + (strcmp (synctest10, "1313113313") == 0 ) + || (strcmp (synctest10, "1113113313") == 0 ) + || (strcmp (synctest10, "1313113333") == 0 ) + // || (strcmp (synctest10, "1113113333") == 0 ) + // || (strcmp (synctest10, "1313133313") == 0 ) ) { diff --git a/src/dsd_gps.c b/src/dsd_gps.c index 174496a..87f4bb6 100644 --- a/src/dsd_gps.c +++ b/src/dsd_gps.c @@ -129,8 +129,8 @@ void lip_protocol_decoder (dsd_opts * opts, dsd_state * state, uint8_t * input) if (opts->lrrp_file_output == 1) { - char * datestr = getDate(); - char * timestr = getTime(); + char * datestr = getDateS(); + char * timestr = getTimeC(); //open file by name that is supplied in the ncurses terminal, or cli pFile = fopen (opts->lrrp_out_file, "a"); @@ -247,8 +247,8 @@ void nmea_iec_61162_1 (dsd_opts * opts, dsd_state * state, uint8_t * input, uint if (opts->lrrp_file_output == 1) { - char * datestr = getDate(); - char * timestr = getTime(); + char * datestr = getDateS(); + char * timestr = getTimeC(); int s = (int)fkph; //rounded interger format for the log report int a = 0; @@ -406,8 +406,8 @@ void harris_lptt (dsd_opts * opts, dsd_state * state, uint8_t * input, uint32_t if (opts->lrrp_file_output == 1 && src != 0 && gps_quality != 3) { - char * datestr = getDate(); - char * timestr = getTime(); + char * datestr = getDateS(); + char * timestr = getTimeC(); //rounded interger formats for the log report int s = (int)fkph; @@ -536,8 +536,8 @@ void dmr_embedded_gps (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[]) if (opts->lrrp_file_output == 1) { - char * datestr = getDate(); - char * timestr = getTime(); + char * datestr = getDateS(); + char * timestr = getTimeC(); //open file by name that is supplied in the ncurses terminal, or cli pFile = fopen (opts->lrrp_out_file, "a"); @@ -644,8 +644,9 @@ void apx_embedded_gps (dsd_opts * opts, dsd_state * state, uint8_t lc_bits[]) FILE * pFile; //file pointer if (opts->lrrp_file_output == 1) { - char * datestr = getDate(); - char * timestr = getTime(); + + char * datestr = getDateS(); + char * timestr = getTimeC(); //open file by name that is supplied in the ncurses terminal, or cli pFile = fopen (opts->lrrp_out_file, "a"); @@ -719,11 +720,235 @@ void decode_ars(dsd_opts * opts, dsd_state * state, uint8_t * input, int len) UNUSED(len); } +//WIP: Based on Denny's always brilliant reverse-engineering of GPS reports void nxdn_gps_report(dsd_opts * opts, dsd_state * state, uint8_t * input, uint32_t src) { - //TODO: This - UNUSED(opts); - UNUSED(state); - UNUSED(input); - UNUSED(src); + + // Basic fields + // uint8_t quality = (uint8_t)convert_bits_into_output(input+8, 4); //can't seem to work these first two out correctly + // uint8_t num_sat = (uint8_t)convert_bits_into_output(input+12, 4); //can't seem to work these first two out correctly + int16_t elevation = (int16_t)convert_bits_into_output(input+56, 16); + + // Speed & Heading (in tenths) + uint16_t speed_raw = (uint16_t)convert_bits_into_output(input+74, 14); + uint16_t heading_raw = (uint16_t)convert_bits_into_output(input+92, 12); + double speed = speed_raw / 10.0; //knots or k/h + double heading = heading_raw / 10.0; + + //Date + uint16_t year = (uint16_t)(convert_bits_into_output(input+136,7) + 2000); + uint8_t month = (uint8_t) convert_bits_into_output(input+143,4); + uint8_t day = (uint8_t)(convert_bits_into_output(input+147,5) + 1); + + // Longitude (DDMM.mmmm format) + uint16_t lon_degmin = (uint16_t)convert_bits_into_output(input+152, 16); + uint16_t lon_frac = (uint16_t)convert_bits_into_output(input+16, 15); + uint8_t lon_hem = (uint8_t) convert_bits_into_output(input+183, 1); + double lon_minutes = (lon_degmin % 100) + (lon_frac / 10000.0); + double lon_decimal = (lon_degmin / 100) + (lon_minutes / 60.0); + double longitude = (lon_hem == 0) ? lon_decimal : -lon_decimal; + + // Latitude (DDMM.mmmm format) + uint16_t lat_degmin = (uint16_t)convert_bits_into_output(input+184,16); + uint16_t lat_frac = (uint16_t)convert_bits_into_output(input+200,15); + uint8_t lat_hem = (uint8_t) convert_bits_into_output(input+215,1); + double lat_minutes = (lat_degmin % 100) + (lat_frac / 10000.0); + double lat_decimal = (lat_degmin / 100) + (lat_minutes / 60.0); + double latitude = (lat_hem == 0) ? lat_decimal : -lat_decimal; + + //Time + // uint8_t hour = (uint8_t) convert_bits_into_output(input+247,5); + // uint8_t minute = (uint8_t) convert_bits_into_output(input+252,6); + // uint8_t second = (uint8_t) convert_bits_into_output(input+258,6); //this seems to be zero, recheck time + + char deg_glyph[4]; + sprintf (deg_glyph, "%s", "°"); + + //Report + fprintf (stderr, "\n GPS: (%f%s, %f%s) ", latitude, deg_glyph, longitude, deg_glyph); + fprintf (stderr, "Speed: %lf k/h; ", speed); + fprintf (stderr, "COG: %lf; ", heading); + fprintf (stderr, "Elevation: %i; ", elevation); + // fprintf (stderr, "Quality: %d / %d; ", quality, num_sat); + fprintf (stderr, "Date: %04d/%02d/%02d ", year, month, day); + // fprintf (stderr, "Time: %02d:%02d:%02d ", hour, minute, second); + + //save to LRRP report for mapping/logging + if (opts->lrrp_file_output == 1 && src != 0) //&& quality? + { + + char * datestr = getDateS(); + char * timestr = getTimeC(); + + //rounded interger formats for the log report + int s = (int)speed; + int a = (int)heading; + + //open file by name that is supplied in the ncurses terminal, or cli + FILE * pFile; //file pointer + pFile = fopen (opts->lrrp_out_file, "a"); + fprintf (pFile, "%s\t", datestr ); + fprintf (pFile, "%s\t", timestr ); + fprintf (pFile, "%08d\t", src); + fprintf (pFile, "%.6lf\t", latitude); + fprintf (pFile, "%.6lf\t", longitude); + fprintf (pFile, "%d\t ", s); + fprintf (pFile, "%d\t ", a); + fprintf (pFile, "\n"); + fclose (pFile); + + if (timestr != NULL) + { + free (timestr); + timestr = NULL; + } + if (datestr != NULL) + { + free (datestr); + datestr = NULL; + } + + } + + //reset source and target from PDU storage + state->dmr_lrrp_source[0] = 0; + state->dmr_lrrp_target[0] = 0; +} + +uint8_t nmea_sentence_checker(dsd_opts * opts, dsd_state * state, uint8_t * input, uint8_t slot, int len) +{ + + //Check Start and End, and Validity w/ Checksum + int i; + uint8_t start_value = (uint8_t)ConvertBitIntoBytes(input, 8); + uint8_t end_value = 0; + uint8_t checksum_calc = 0; + uint8_t checksum_ext = 0; + char checksum_str[3]; + memset(checksum_str, 0, sizeof(checksum_str)); + uint8_t valid = 0; + + if (start_value == '$' || start_value == '!') + { + //debug + // fprintf (stderr, "Start Okay! "); + + for (i = 1; i < len; i++) + { + uint8_t value = (uint8_t)ConvertBitIntoBytes(input+(i*8), 8); + if (value == '*') + { + end_value = value; + checksum_str[0] = (uint8_t)ConvertBitIntoBytes(input+(i*8)+8, 8); + checksum_str[1] = (uint8_t)ConvertBitIntoBytes(input+(i*8)+16, 8); + checksum_str[2] = 0; + sscanf (checksum_str, "%hhX", &checksum_ext); + break; + } + else if (value >= 0x20 && value < 0x7F) + { + checksum_calc ^= value; + continue; + } + else break; //anything else that isn't ascii range values + } + + //debug + // if (end_value == '*') + // fprintf (stderr, "Checksum Present! "); + + if (checksum_ext == checksum_calc) + { + // fprintf (stderr, "Checksum Okay! (%02X / %02X)", checksum_calc, checksum_ext); + valid = 1; + } + else + { + // fprintf (stderr, "Checksum Err! (%02X / %02X) ", checksum_calc, checksum_ext); + valid = 0; + } + + } + + + //dump, if valid + memset(state->event_history_s[slot].Event_History_Items[0].text_message, 0, sizeof(state->event_history_s[slot].Event_History_Items[0].text_message)); + if (valid) + { + uint8_t ascii = 0; + uint16_t crlf = 0; + for (i = 0; i < len; i++) + { + ascii = (uint8_t)ConvertBitIntoBytes(input+(i*8), 8); + crlf = (uint16_t)ConvertBitIntoBytes(input+(i*8), 16); + if (ascii >= 0x20 && ascii < 0x7F) + { + fprintf (stderr, "%c", ascii); + state->event_history_s[slot].Event_History_Items[0].text_message[i] = ascii; + } + else if (crlf == 0x0D0A) //NMEA Sentences end with 0x0D 0x0A (CR/LF) + { + fprintf (stderr, "%s", " "); + state->event_history_s[slot].Event_History_Items[0].text_message[i+0] = 0x20; //Space + state->event_history_s[slot].Event_History_Items[0].text_message[i+1] = 0x20; //Space + i+=2; + break; + } + else break; //anything else that isn't ascii range values + } + + //check for supplimentary string i.e., $PP25 + ascii = (uint8_t)ConvertBitIntoBytes(input+(i*8), 8); + if (crlf == 0x0D0A && ascii == '$') + { + for (i = i; i < len; i++) + { + ascii = (uint8_t)ConvertBitIntoBytes(input+(i*8), 8); + crlf = (uint16_t)ConvertBitIntoBytes(input+(i*8), 16); + + if (ascii >= 0x20 && ascii < 0x7F) + { + fprintf (stderr, "%c", ascii); + state->event_history_s[slot].Event_History_Items[0].text_message[i] = ascii; + } + else if (crlf == 0x0D0A) //Supplimentary Sentences end with 0x0D 0x0A (CR/LF) + { + state->event_history_s[slot].Event_History_Items[0].text_message[i] = 0x20; + break; + } + else break; //anything else that isn't ascii range values + } + + } + + //debug + // fprintf(stderr, "\n %s", state->event_history_s[slot].Event_History_Items[0].text_message); + + //dump to event history + state->event_history_s[slot].Event_History_Items[0].text_message[i] = '\0'; + uint32_t source = state->dmr_lrrp_source[slot]; + uint32_t target = state->dmr_lrrp_target[slot]; + char comp_string[500]; memset (comp_string, 0, sizeof(comp_string)); + sprintf (comp_string, "NMEA SRC: %d; TGT: %d;", source, target); + watchdog_event_datacall (opts, state, source, target, comp_string, slot); + } + + else + { + if (start_value != '$' && start_value != '!') + fprintf (stderr, " Not an NMEA Sentence Structure;"); + else if ( (start_value == '$' || start_value == '!') && end_value != '*') + fprintf (stderr, " Possible NMEA Sentence, Missing Ending *;"); + else if ( start_value != '$' && start_value != '!' && end_value == '*') + fprintf (stderr, " Possible NMEA Sentence, Missing Starting $ or !;"); + else if (checksum_calc != checksum_ext) + fprintf (stderr, " NMEA Checksum Error (%02X / %02X);", checksum_calc, checksum_ext); + } + + //TODO: Convert to LRRP data and report / GPS on Event History + + state->dmr_lrrp_source[slot] = 0; + state->dmr_lrrp_target[slot] = 0; + + return valid; } diff --git a/src/nxdn_element.c b/src/nxdn_element.c index 567c589..cf6afbb 100644 --- a/src/nxdn_element.c +++ b/src/nxdn_element.c @@ -186,6 +186,10 @@ void NXDN_Elements_Content_decode(dsd_opts * opts, dsd_state * state, uint8_t Cr //reset header validity state->data_header_valid[0] = 0; + //set source and target + state->dmr_lrrp_source[0] = 0; + state->dmr_lrrp_target[0] = 0; + //reset encryption variables (for pdu) state->payload_algid = 0; state->payload_keyid = 0; @@ -220,6 +224,10 @@ void NXDN_Elements_Content_decode(dsd_opts * opts, dsd_state * state, uint8_t Cr //reset header validity state->data_header_valid[0] = 0; + //set source and target + state->dmr_lrrp_source[0] = 0; + state->dmr_lrrp_target[0] = 0; + //reset encryption variables (for pdu) state->payload_algid = 0; state->payload_keyid = 0; @@ -712,23 +720,6 @@ void NXDN_decode_VCALL_ASSGN(dsd_opts * opts, dsd_state * state, uint8_t * Messa state->p25_vc_freq[0] = state->p25_vc_freq[1] = freq; opts->p25_is_tuned = 1; //set to 1 to set as currently tuned so we don't keep tuning nonstop - //set rid and tg when we actually tune to it - //only assign rid if not spare and not reserved (happens on private calls, unsure of its significance) - if ( (VoiceCallOption & 0xF) < 4) //ideally, only want 0, 2, or 3 - state->nxdn_last_rid = SourceUnitID; - state->nxdn_last_tg = DestinationID; - sprintf (state->nxdn_call_type, "%s", NXDN_Call_Type_To_Str(CallType)); - - if (CallType == 3) - { - state->gi[0] = 1; //Private Call - //unassign these, sometimes, when trunking, these may be reversed by the time listened, - //and this will plant an extra private call in the event_history - state->nxdn_last_rid = 0; - state->nxdn_last_tg = 0; - } - else state->gi[0] = 0; //Group Call - //Call String for Per Call WAV File sprintf (state->call_string[0], "%s", NXDN_Call_Type_To_Str(CallType)); if (CCOption & 0x80) strcat (state->call_string[0], " Emergency"); @@ -760,23 +751,6 @@ void NXDN_decode_VCALL_ASSGN(dsd_opts * opts, dsd_state * state, uint8_t * Messa state->p25_vc_freq[0] = state->p25_vc_freq[1] = freq; opts->p25_is_tuned = 1; - //set rid and tg when we actually tune to it - //only assign rid if not spare and not reserved (happens on private calls, unsure of its significance) - if ( (VoiceCallOption & 0xF) < 4) //ideally, only want 0, 2, or 3 - state->nxdn_last_rid = SourceUnitID; - state->nxdn_last_tg = DestinationID; - sprintf (state->nxdn_call_type, "%s", NXDN_Call_Type_To_Str(CallType)); - - if (CallType == 3) - { - state->gi[0] = 1; //Private Call - //unassign these, sometimes, when trunking, these may be reversed by the time listened, - //and this will plant an extra private call in the event_history - state->nxdn_last_rid = 0; - state->nxdn_last_tg = 0; - } - else state->gi[0] = 0; //Group Call - //Call String for Per Call WAV File sprintf (state->call_string[0], "%s", NXDN_Call_Type_To_Str(CallType)); if (CCOption & 0x80) strcat (state->call_string[0], " Emergency"); @@ -2586,6 +2560,10 @@ void nxdn_sdcall_header(dsd_opts * opts, dsd_state * state, uint8_t * Message) //set this is a valid header state->data_header_valid[0] = 1; + + //set source and target + state->dmr_lrrp_source[0] = source; + state->dmr_lrrp_target[0] = target; } @@ -2659,7 +2637,7 @@ void nxdn_dcall_header(dsd_opts * opts, dsd_state * state, uint8_t * Message) NXDN_Data_Call_Option_To_Str(dcall_opt, duplex_string, dcall_string); fprintf (stderr, "\n %s", KCYN); - fprintf (stderr, "Data Call Header (%04X) ", pkt_info); + fprintf (stderr, "Data Call Header (%06X) ", pkt_info); if (idas == 0) fprintf (stderr, "Source: %d; Target: %d; ", source, target); else @@ -2732,6 +2710,10 @@ void nxdn_dcall_header(dsd_opts * opts, dsd_state * state, uint8_t * Message) //set this is a valid header state->data_header_valid[0] = 1; + + //set source and target + state->dmr_lrrp_source[0] = source; + state->dmr_lrrp_target[0] = target; } @@ -2739,8 +2721,9 @@ int nxdn_dcall_data(dsd_opts * opts, dsd_state * state, int type, uint8_t * Mess { UNUSED(opts); - //packet frame number and block number, observation shows these seem to be the same value (observed Type-D) - //but the manual does not quite explain these values indicate super packet of up to 16 big packets, and 1 packet is up to 16 blocks? + //if this is a full packet delivery, both of these numbers match typically, + //but if this is a selective retry resending blocks, the second number is the + //number of the retry block, and the first number is the remaining blocks uint8_t pf_num = (uint8_t)ConvertBitIntoBytes(Message+8, 4); uint8_t blk_num = (uint8_t)ConvertBitIntoBytes(Message+12, 4); @@ -2783,6 +2766,18 @@ int nxdn_dcall_data(dsd_opts * opts, dsd_state * state, int type, uint8_t * Mess return -1; } + //Part 1-B Basic Operation Ver.2.0 page 34 + //if this is a retry, we don't have memory of the other pieces, so just nuke it + //although, I suppose its possible that, for example, there were originally 4 blocks, + //but the retry is going to retry 3,3,2,0 blocks all sequentially, or similar, + //and this would potentially miss it, but the CRC32 will determine success then. + if (pf_num != blk_num) + { + fprintf (stderr, "Partial Selective Retry, Previous Delivery Not Retained in Memory; "); + state->data_header_valid[0] = 0; + return -1; + } + if (total_bytes < 4) //this shouldn't even happen in code { fprintf (stderr, "Total Bytes After Padding Less Than Required; "); @@ -2797,7 +2792,7 @@ int nxdn_dcall_data(dsd_opts * opts, dsd_state * state, int type, uint8_t * Mess memcpy(state->dmr_pdu_sf[0]+ptr, Message+16, byte_len*8*sizeof(uint8_t)); //Finished Assembly - if (blk_num == 0) + if (pf_num == 0) { //KS creation and application, if applicable @@ -2933,18 +2928,13 @@ int nxdn_dcall_data(dsd_opts * opts, dsd_state * state, int type, uint8_t * Mess if (crc_ext == crc_chk) { uint8_t opcode = (uint8_t)ConvertBitIntoBytes(state->dmr_pdu_sf[0], 8); + uint8_t nmea = (uint8_t)ConvertBitIntoBytes(state->dmr_pdu_sf[0]+8, 8); uint32_t reverse = (uint8_t)ConvertBitIntoBytes(state->dmr_pdu_sf[0], 24); //observed 3 bytes of zeroes - if (opcode == 0x06) + if (opcode == 0x06 && (nmea == '$' || nmea == '!')) //may only need to check one of the other { - //String Data Dump if first byte is 0x06 (NMEA sentences observed here) + //Check NMEA Sentence fprintf (stderr, "\n "); - for (int i = 1; i < total_bytes; i++) - { - uint8_t ascii = (uint8_t)ConvertBitIntoBytes(state->dmr_pdu_sf[0]+(i*8), 8); - if (ascii >= 0x20 && ascii < 0x7F) - fprintf (stderr, "%c", ascii); - else break; - } + nmea_sentence_checker(opts, state, state->dmr_pdu_sf[0]+8, 0, total_bytes); } else if (reverse == 0) { @@ -2962,7 +2952,7 @@ int nxdn_dcall_data(dsd_opts * opts, dsd_state * state, int type, uint8_t * Mess //Debug Data Dump fprintf (stderr, "\n REV: "); - for (int i = 0; i < total_bytes-4-4+1; i++) //sans the CRC and the leading zeroes + for (int i = 0; i < total_bytes-7; i++) //sans the CRC and the leading zeroes fprintf (stderr, "%02X", reverse_bytes[i]); uint8_t reverse_byte_bits[total_bytes*8]; @@ -2971,7 +2961,7 @@ int nxdn_dcall_data(dsd_opts * opts, dsd_state * state, int type, uint8_t * Mess //GPS report on 0xFFFC? or is that just any odd Kenwood message? if ((uint16_t)convert_bits_into_output(reverse_byte_bits, 16) == 0xFFFC) - nxdn_gps_report(opts, state, reverse_byte_bits+16, state->nxdn_last_rid); //src is probably reset on cac + nxdn_gps_report(opts, state, reverse_byte_bits+16, state->dmr_lrrp_source[0]); } else @@ -3000,6 +2990,10 @@ int nxdn_dcall_data(dsd_opts * opts, dsd_state * state, int type, uint8_t * Mess //reset header validity state->data_header_valid[0] = 0; + //set source and target + state->dmr_lrrp_source[0] = 0; + state->dmr_lrrp_target[0] = 0; + //reset encryption variables state->payload_algid = 0; state->payload_keyid = 0; diff --git a/src/p25p1_pdu_data.c b/src/p25p1_pdu_data.c index a3e8e5e..f88051f 100644 --- a/src/p25p1_pdu_data.c +++ b/src/p25p1_pdu_data.c @@ -457,8 +457,18 @@ void p25_decode_pdu_data(dsd_opts * opts, dsd_state * state, uint8_t * input, in if (sap == 0 || sap == 4) //User Data or Packet Data (both are UDP typically, same format dmr UDP/IP data) decode_ip_pdu (opts, state, len+1, input+ptr); - else if (sap == 48) //Tier 1 Location Service (or does it depend on the io bit?) - utf8_to_text(state, 1, len-ptr+1, input+ptr); //TODO, read initial string, i.e., $GPRMC and properly decode + //NMEA Tier 1 Location Reporting w/ Supplimentary $PP25 string + else if (sap == 48) + { + len += 12; //not sure how we ended up off by up to 12 on this, above negation too much? + state->dmr_lrrp_source[state->currentslot] = state->lastsrc; + state->dmr_lrrp_source[state->currentslot] = state->lasttg; + uint8_t input_bits[(len+1)*8]; + memset(input_bits, 0, sizeof(input_bits)); + unpack_byte_array_into_bit_array(input, input_bits, len); + fprintf (stderr, "\n "); + nmea_sentence_checker(opts, state, input_bits+ptr*8, state->currentslot, len*8); + } // else //default catch all (debug only) // {