/*------------------------------------------------------------------------------- * dsd_ncurses.c * DSD-FME ncurses terminal printer with menu system * * ASCII art generated by: * https://fsymbols.com/generators/carty/ * * LWVMOBILE * 2022-08 DSD-FME Florida Man Edition * * ilyacodes * 2024-03 EDACS-FME display improvements *-----------------------------------------------------------------------------*/ #include "dsd.h" #include "git_ver.h" #ifdef USE_RTLSDR #include //use to list out all detected RTL dongles char vendor[256], product[256], serial[256], userdev[256]; int device_count = 0; #endif uint32_t temp_freq = -1; //struct for checking existence of directory to write to struct stat st_wav = {0}; int reset = 0; uint8_t eh_slot = 0; //testing only, have a toggle slot for history uint8_t eh_off = 0; //testing only, index offset for scrolling history int lls = -1; int i = 0; char versionstr[25]; unsigned long long int edacs_channel_tree[33][6]; char * FM_bannerN[9] = { " ", " ██████╗ ██████╗██████╗    ███████╗███╗ ███╗███████╗ ", " ██╔══██╗██╔════╝██╔══██╗   ██╔════╝████╗ ████║██╔════╝ ", " ██║ ██║╚█████╗ ██║ ██║   █████╗ ██╔████╔██║█████╗ ", " ██║ ██║ ╚═══██╗██║ ██║   ██╔══╝ ██║╚██╔╝██║██╔══╝ ", " ██████╔╝██████╔╝██████╔╝   ██║ ██║ ╚═╝ ██║███████╗ ", " ╚═════╝ ╚═════╝ ╚═════╝    ╚═╝ ╚═╝ ╚═╝╚══════╝ ", " ", " " }; char * SyncTypes[44] = { "P25p1", "P25p1", "X2TDMA DATA", "X2TDMA DATA", "X2TDMA VOICE", "X2TDMA VOICE", "DSTAR", //voice "DSTAR", //voice "M17", "M17", "DMR", //10 "DMR", "DMR", "DMR", "EDACS/PV", "EDACS/PV", "M17", //M17 Voice Stream "M17", //M17 Voice Stream "DSTAR", //header "DSTAR", //header "dPMR", //20 "dPMR", "dPMR", "dPMR", "dPMR", "dPMR", "dPMR", "dPMR", "NXDN", "NXDN", "YSF", //30 "YSF", //31 "DMR", "DMR", "DMR", "P25p2", "P25p2", "EDACS/PV", //37 "EDACS/PV", //38 "ANALOG", //39 Generic Sync Types "DIGITAL" //40 Generic Sync Types }; char * DMRBusrtTypes[32] = { "PI ", "VLC ", "TLC ", "CSBK ", "MBCH ", "MBCC ", "DATA ", "R12D ", "R34D ", "IDLE ", "R1_D ", "ERR ", "DUID ERR ", "R-S ERR ", "CRC ERR ", "NULL ", "VOICE", " ", "INIT ", "INIT ", "PTT", //20 MAC "VOICE", //21 MAC_ACTIVE "HANGTIME", //22 MAC "PTT END", //23 MAC "IDLE", //24 MAC "HDU", "VOICE", //26 LDU1 "VOICE", //27 LDU2 "TDU/LC", "TSBK", "SIGNAL", //MAC_SIGNAL "SIGNAL" //MAC_SIGNAL }; void beeper (dsd_opts * opts, dsd_state * state, int lr, int id, int ad, int len) { UNUSED(state); int i, j, n; //use lr as left or right channel designation in stereo config float samp_f[160]; //mono float sample float samp_fs[320]; //stereo float sample short samp_s[160]; //mono short sample short samp_ss[320]; //stereo short sample short samp_su[960]; //mono short upsample short outbuf[6]; //temp storage for upsample n = 0; //rolling sine wave 'degree' //double len if not using Pulse Audio, //anything over UDP or using OSS may //not clear the buffer at the shorter len if (opts->audio_out_type != 0) len *= 2; //each j increment is 20 ms at 160 samples / 8 kHz for (j = 0; j < len; j++) { //'zero' out stereo mix samples memset (samp_fs, 0, sizeof(samp_fs)); memset (samp_ss, 0, sizeof(samp_ss)); //generate a tone with supplied tone ID and AD value soft_tonef(samp_f, n, id, ad); //convert float to short if required if (opts->floating_point == 0) { mbe_floattoshort(samp_f, samp_s); for (i = 0; i < 160; i++) { samp_s[i] *= 4000; //apply gain samp_ss[(i*2)+lr] = samp_s[i]; } } //load returned tone sample into appropriate channel -- left = +0; right = +1; for (i = 0; i < 160; i++) samp_fs[(i*2)+lr] = samp_f[i]; //play sample 3 times (20ms x 3 = 60ms) if (opts->audio_out_type == 0) //Pulse Audio { if (opts->pulse_digi_out_channels == 2 && opts->floating_point == 1) pa_simple_write(opts->pulse_digi_dev_out, samp_fs, 320*4, NULL); if (opts->pulse_digi_out_channels == 1 && opts->floating_point == 1) pa_simple_write(opts->pulse_digi_dev_out, samp_f, 160*4, NULL); if (opts->pulse_digi_out_channels == 2 && opts->floating_point == 0) pa_simple_write(opts->pulse_digi_dev_out, samp_ss, 320*2, NULL); if (opts->pulse_digi_out_channels == 1 && opts->floating_point == 0) pa_simple_write(opts->pulse_digi_dev_out, samp_s, 160*2, NULL); } else if (opts->audio_out_type == 8) //UDP Audio { if (opts->pulse_digi_out_channels == 2 && opts->floating_point == 1) udp_socket_blaster (opts, state, 320*4, samp_fs); if (opts->pulse_digi_out_channels == 1 && opts->floating_point == 1) udp_socket_blaster (opts, state, 160*4, samp_f); if (opts->pulse_digi_out_channels == 2 && opts->floating_point == 0) udp_socket_blaster (opts, state, 320*2, samp_ss); if (opts->pulse_digi_out_channels == 1 && opts->floating_point == 0) udp_socket_blaster (opts, state, 160*2, samp_s); } else if (opts->audio_out_type == 1) //STDOUT { if (opts->pulse_digi_out_channels == 2 && opts->floating_point == 1) write(opts->audio_out_fd, samp_fs, 320*4); if (opts->pulse_digi_out_channels == 1 && opts->floating_point == 1) write(opts->audio_out_fd, samp_f, 160*4); if (opts->pulse_digi_out_channels == 2 && opts->floating_point == 0) write(opts->audio_out_fd, samp_ss, 320*2); if (opts->pulse_digi_out_channels == 1 && opts->floating_point == 0) write(opts->audio_out_fd, samp_s, 160*2); } else if (opts->audio_out_type == 2) //OSS Variable Output (no float) { if (opts->pulse_digi_out_channels == 2 && opts->floating_point == 0) write(opts->audio_out_fd, samp_ss, 320*2); if (opts->pulse_digi_out_channels == 1 && opts->floating_point == 0) write(opts->audio_out_fd, samp_s, 160*2); } else if (opts->audio_out_type == 5) //OSS 48k/1 configuration with upsample { short prev = 0; for (i = 0; i < 160; i++) { upsampleS (samp_s[i], prev, outbuf); samp_su[(i*6)+0] = outbuf[0]; samp_su[(i*6)+1] = outbuf[1]; samp_su[(i*6)+2] = outbuf[2]; samp_su[(i*6)+3] = outbuf[3]; samp_su[(i*6)+4] = outbuf[4]; samp_su[(i*6)+5] = outbuf[5]; } write (opts->audio_out_fd, samp_su, 960*2); } } } time_t nowN; int isCustomAfsString(dsd_state * state) { return state->edacs_a_bits != 4 || state->edacs_f_bits != 4 || state->edacs_s_bits != 3; } //Get the string length we need for an AFS string, math-style int getAfsStringLength(dsd_state * state) { if (!isCustomAfsString(state)) return 6; int length = 0; length += (state->edacs_a_bits + 2) / 3; length += (state->edacs_f_bits + 2) / 3; length += (state->edacs_s_bits + 2) / 3; length += 2; //colon separators // This will be either 6 or 7 return length; } //Format the AFS string, Florida-style int getAfsString(dsd_state * state, char * buffer, int a, int f, int s) { if (!isCustomAfsString(state)) { sprintf(buffer, "%02d-%02d%01d", a, f, s); return 6; } int printed_chars = 0; switch (state->edacs_a_bits) { case 1: case 2: case 3: sprintf(buffer, "%01d:", a); printed_chars += 1; break; case 4: case 5: case 6: sprintf(buffer, "%02d:", a); printed_chars += 2; break; case 7: case 8: case 9: sprintf(buffer, "%03d:", a); printed_chars += 3; break; } sprintf(buffer + printed_chars, ":"); printed_chars++; switch (state->edacs_f_bits) { case 1: case 2: case 3: sprintf(buffer + printed_chars, "%01d", f); printed_chars += 1; break; case 4: case 5: case 6: sprintf(buffer + printed_chars, "%02d", f); printed_chars += 2; break; case 7: case 8: case 9: sprintf(buffer + printed_chars, "%03d", f); printed_chars += 3; break; } sprintf(buffer + printed_chars, ":"); printed_chars++; switch (state->edacs_s_bits) { case 1: case 2: case 3: sprintf(buffer + printed_chars, "%01d", s); printed_chars += 1; break; case 4: case 5: case 6: sprintf(buffer + printed_chars, "%02d", s); printed_chars += 2; break; case 7: case 8: case 9: sprintf(buffer + printed_chars, "%03d", s); printed_chars += 3; break; } return printed_chars; } //testing a few things, going to put this into ncursesMenu #define WIDTH 36 #define HEIGHT 25 int startx = 0; int starty = 0; char *choicesc[] = { "Return", " ", //Save Decoded Audio WAV (Legacy Mode) //Disabled "Save Signal to Symbol Capture Bin", "Toggle Muting Encrypted Traffic ", "Save Per Call Decoded WAV", "Setup and Start RTL Input ", "Retune RTL Dongle ", "Toggle C4FM/QPSK (P2 TDMA CC)", "Toggle C4FM/QPSK (P1 FDMA CC)", "Start TCP Direct Link Audio", "Configure RIGCTL", "Stop All Decoded WAV Saving", "Read OP25/FME Symbol Capture Bin", "Replay Last Symbol Capture Bin", "Stop & Close Symbol Capture Bin Playback", "Stop & Close Symbol Capture Bin Saving", "Toggle Call Alert Beep ", "Resume Decoding" }; //make tweaks for TDMA and AUTO? char *choices[] = { "Resume Decoding", "Decode AUTO", "Decode TDMA", "Decode DSTAR", "Decode M17", "Decode EDACS/PV", "Decode P25p2", "Decode dPMR", "Decode NXDN48", "Decode NXDN96", "Decode DMR", "Decode YSF", "Toggle Signal Inversion", "Key Entry", "Reset Event History", "Toggle Payloads to Console", "Manually Set p2 Parameters", //16 "Input & Output Options", "LRRP Data to File", "Exit DSD-FME", }; int n_choices = sizeof(choices) / sizeof(char *); // int n_choicesb = sizeof(choicesb) / sizeof(char *); int n_choicesc = sizeof(choicesc) / sizeof(char *); void print_menu(WINDOW *menu_win, int highlight) { int x, y, i; x = 2; y = 2; box(menu_win, 0, 0); for(i = 0; i < n_choices; ++i) { if(highlight == i + 1) /* High light the present choice */ { wattron(menu_win, A_REVERSE); mvwprintw(menu_win, y, x, "%s", choices[i]); wattroff(menu_win, A_REVERSE); } else mvwprintw(menu_win, y, x, "%s", choices[i]); ++y; } wrefresh(menu_win); } void print_menuc(WINDOW *menu_win, int highlight) { int x, y, i; x = 2; y = 2; box(menu_win, 0, 0); for(i = 0; i < n_choicesc; ++i) { if(highlight == i + 1) /* High light the present choice */ { wattron(menu_win, A_REVERSE); mvwprintw(menu_win, y, x, "%s", choicesc[i]); wattroff(menu_win, A_REVERSE); } else mvwprintw(menu_win, y, x, "%s", choicesc[i]); ++y; } wrefresh(menu_win); } //end Testing void ncursesOpen (dsd_opts * opts, dsd_state * state) { UNUSED(opts); UNUSED(state); // state->menuopen = 1; //flag the menu is open, stop processing getFrameSync mbe_printVersion (versionstr); setlocale(LC_ALL, ""); initscr(); //Initialize NCURSES screen window start_color(); #ifdef PRETTY_COLORS init_pair(1, COLOR_YELLOW, COLOR_BLACK); //Yellow/Amber for frame sync/control channel, NV style init_pair(2, COLOR_RED, COLOR_BLACK); //Red for Terminated Calls init_pair(3, COLOR_GREEN, COLOR_BLACK); //Green for Active Calls init_pair(4, COLOR_CYAN, COLOR_BLACK); //Cyan for Site Extra and Patches init_pair(5, COLOR_MAGENTA, COLOR_BLACK); //Magenta for no frame sync/signal init_pair(6, COLOR_WHITE, COLOR_BLACK); //White Card Color Scheme #else init_pair(1, COLOR_WHITE, COLOR_BLACK); //White Card Color Scheme init_pair(2, COLOR_WHITE, COLOR_BLACK); //White Card Color Scheme init_pair(3, COLOR_WHITE, COLOR_BLACK); //White Card Color Scheme init_pair(4, COLOR_WHITE, COLOR_BLACK); //White Card Color Scheme init_pair(5, COLOR_WHITE, COLOR_BLACK); //White Card Color Scheme init_pair(6, COLOR_WHITE, COLOR_BLACK); //White Card Color Scheme #endif noecho(); cbreak(); } //ncursesMenu void ncursesMenu (dsd_opts * opts, dsd_state * state) { //update sync time on cc sync so we don't immediately go CC hunting when exiting the menu state->last_cc_sync_time = time(NULL); //close pulse output if not null output if (opts->audio_out == 1 && opts->audio_out_type == 0) { closePulseOutput (opts); } //close OSS output if (opts->audio_out_type == 2 || opts->audio_out_type == 5) { close (opts->audio_out_fd); } if (opts->audio_in_type == 0) //close pulse input if it is the specified input method { closePulseInput(opts); } if (opts->audio_in_type == 3) { #ifdef USE_RTLSDR rtl_clean_queue(); #endif } if (opts->audio_in_type == 8) //close TCP input SF file so we don't buffer audio while not decoding { sf_close(opts->tcp_file_in); } state->payload_keyid = 0; state->payload_keyidR = 0; //zero out state->nxdn_last_tg = 0; state->nxdn_last_ran = -1; //0 state->nxdn_last_rid = 0; //0 WINDOW *menu_win; WINDOW *test_win; WINDOW *entry_win; WINDOW *info_win; int highlight = 1; int highlightc = 1; int choice = 0; int choicec = 0; int c; int e; startx = 2; starty = 1; menu_win = newwin(HEIGHT, WIDTH, starty, startx); keypad(menu_win, TRUE); mvprintw(0, 0, " Use arrow keys to go up and down, Press ENTER to select a choice."); refresh(); print_menu(menu_win, highlight); while(1) { c = wgetch(menu_win); switch(c) { case KEY_UP: if(highlight == 1) highlight = n_choices; else --highlight; break; case KEY_DOWN: if(highlight == n_choices) highlight = 1; else ++highlight; break; case 10: choice = highlight; break; default: //mvprintw(24, 0, "Character pressed is = %3d Hopefully it can be printed as '%c'", c, c); refresh(); break; } print_menu(menu_win, highlight); if (highlight == 2) { info_win = newwin(7, WIDTH+18, starty, startx+20); box (info_win, 0, 0); mvwprintw(info_win, 2, 2, " AUTO Decoding Class Supports the following:"); mvwprintw(info_win, 3, 2, " P25p1, YSF, DSTAR, X2-TDMA and DMR"); mvwprintw(info_win, 4, 2, " C4FM or QPSK @ 4800bps (no H8D-QPSK)"); wrefresh(info_win); } if (highlight == 3) { info_win = newwin(7, WIDTH+18, starty, startx+20); box (info_win, 0, 0); mvwprintw(info_win, 2, 2, " TDMA Trunking Class Supports the following:"); mvwprintw(info_win, 3, 2, " P25p1 Voice/Control, P25p2 Traffic, and DMR"); mvwprintw(info_win, 4, 2, " C4FM or QPSK @ 4800 or 6000 (no H8D-QPSK)"); wrefresh(info_win); } if (highlight == 7) { info_win = newwin(7, WIDTH+18, starty, startx+20); box (info_win, 0, 0); mvwprintw(info_win, 2, 2, " P25p2 Control (MAC_SIGNAL) or Single Voice Freq."); mvwprintw(info_win, 3, 2, " NOTE: Manually set WACN/SYSID/CC on Voice Only"); mvwprintw(info_win, 4, 2, " C4FM or QPSK @ 6000 (no H8D-QPSK)"); wrefresh(info_win); } //Input Output Options if (choice == 18) { //test making a new window while other window is open test_win = newwin(HEIGHT-1, WIDTH+9, starty+5, startx+5); keypad(menu_win, FALSE); keypad(test_win, TRUE); mvprintw(0, 0, "Input & Output Options "); refresh(); print_menuc(test_win, highlightc); while(1) { e = wgetch(test_win); switch(e) { case KEY_UP: if(highlightc == 1) highlightc = n_choicesc; else --highlightc; break; case KEY_DOWN: if(highlightc == n_choicesc) highlightc = 1; else ++highlightc; break; case 10: choicec = highlightc; break; default: //mvprintw(24, 0, "Character pressed is = %3d Hopefully it can be printed as '%c'", c, c); refresh(); break; } print_menuc(test_win, highlightc); // if (choicec == 2) //Legacy Decode to single wav file is disabled // { // char * timestr = getTime(); // char * datestr = getDate(); // sprintf (opts->wav_out_file, "%s %s DSD-FME-DECODED.wav", datestr, timestr); // if (timestr != NULL) // { // free (timestr); // timestr = NULL; // } // if (datestr != NULL) // { // free (datestr); // datestr = NULL; // } // openWavOutFile (opts, state); // } if (choicec == 3) { //read in filename for symbol capture bin entry_win = newwin(6, WIDTH+16, starty+10, startx+10); box (entry_win, 0, 0); mvwprintw(entry_win, 2, 2, " Enter FME Symbol Capture Bin Filename"); mvwprintw(entry_win, 3, 3, " "); echo(); refresh(); wscanw(entry_win, "%s", opts->symbol_out_file); //&opts->symbol_out_file noecho(); openSymbolOutFile (opts, state); } if (choicec == 4) { //toggle all mutes if (opts->unmute_encrypted_p25 == 0) { opts->unmute_encrypted_p25 = 1; } else opts->unmute_encrypted_p25 = 0; if (opts->dmr_mute_encL == 0) { opts->dmr_mute_encL = 1; } else opts->dmr_mute_encL = 0; if (opts->dmr_mute_encR == 0) { opts->dmr_mute_encR = 1; } else opts->dmr_mute_encR = 0; } if (choicec == 5) { char wav_file_directory[1024]; sprintf (wav_file_directory, "%s", opts->wav_out_dir); wav_file_directory[1023] = '\0'; if (stat(wav_file_directory, &st_wav) == -1) { fprintf (stderr, "%s wav file directory does not exist\n", wav_file_directory); fprintf (stderr, "Creating directory %s to save decoded wav files\n", wav_file_directory); mkdir(wav_file_directory, 0700); } fprintf (stderr,"\n Per Call Wav File Enabled to Directory: %s;.\n", opts->wav_out_dir); srand(time(NULL)); //seed random for filenames (so two filenames aren't the exact same datetime string on initailization) opts->wav_out_f = open_wav_file(opts->wav_out_dir, opts->wav_out_file, 8000, 0); opts->wav_out_fR = open_wav_file(opts->wav_out_dir, opts->wav_out_fileR, 8000, 0); opts->dmr_stereo_wav = 1; } if (choicec == 6) { int confirm = 0; #ifdef USE_RTLSDR entry_win = newwin(6, WIDTH+6, starty+10, startx+10); box (entry_win, 0, 0); mvwprintw(entry_win, 2, 2, " RTL Device Index or Serial Number:"); mvwprintw(entry_win, 3, 3, " "); echo(); refresh(); wscanw(entry_win, "%d", &opts->rtl_dev_index); noecho(); entry_win = newwin(6, WIDTH+6, starty+10, startx+10); box (entry_win, 0, 0); mvwprintw(entry_win, 2, 2, " RTL Device PPM Error:"); mvwprintw(entry_win, 3, 3, " "); echo(); refresh(); wscanw(entry_win, "%d", &opts->rtlsdr_ppm_error); noecho(); entry_win = newwin(6, WIDTH+18, starty+10, startx+10); box (entry_win, 0, 0); mvwprintw(entry_win, 2, 2, " Frequency in Hz (851.8 MHz is 851800000 Hz): "); mvwprintw(entry_win, 3, 3, " "); echo(); refresh(); wscanw(entry_win, "%d", &opts->rtlsdr_center_freq); noecho(); entry_win = newwin(6, WIDTH+18, starty+10, startx+10); box (entry_win, 0, 0); mvwprintw(entry_win, 2, 2, " BW (8, 12, 24, 48)(12 Recommended): "); mvwprintw(entry_win, 3, 3, " "); echo(); refresh(); wscanw(entry_win, "%d", &opts->rtl_bandwidth); noecho(); entry_win = newwin(6, WIDTH+18, starty+10, startx+10); box (entry_win, 0, 0); mvwprintw(entry_win, 2, 2, " RTL Gain Value (0-49) (0 = AGC): "); mvwprintw(entry_win, 3, 3, " "); echo(); refresh(); wscanw(entry_win, "%d", &opts->rtl_gain_value); noecho(); entry_win = newwin(6, WIDTH+18, starty+10, startx+10); box (entry_win, 0, 0); mvwprintw(entry_win, 2, 2, " RTL UDP Port - Legacy/Optional (Default = 0): "); mvwprintw(entry_win, 3, 3, " "); echo(); refresh(); wscanw(entry_win, "%d", &opts->rtl_udp_port); noecho(); entry_win = newwin(6, WIDTH+18, starty+10, startx+10); box (entry_win, 0, 0); mvwprintw(entry_win, 2, 2, " RTL Vol Multiplier (1,2,3) (Default = 1): "); mvwprintw(entry_win, 3, 3, " "); echo(); refresh(); wscanw(entry_win, "%d", &opts->rtl_volume_multiplier); if (opts->rtl_volume_multiplier > 3 || opts->rtl_volume_multiplier < 0) opts->rtl_volume_multiplier = 1; noecho(); entry_win = newwin(8, WIDTH+22, starty+10, startx+10); box (entry_win, 0, 0); mvwprintw(entry_win, 2, 2, " RTL RMS Squelch Level (NXDN/dPMR/Analog/Raw only): "); mvwprintw(entry_win, 3, 3, " "); echo(); refresh(); wscanw(entry_win, "%d", &opts->rtl_squelch_level); noecho(); //use to list out all detected RTL dongles device_count = rtlsdr_get_device_count(); if (!device_count) { fprintf(stderr, "No supported devices found.\n"); } else fprintf(stderr, "Found %d device(s):\n", device_count); for (int i = 0; i < device_count; i++) { rtlsdr_get_device_usb_strings(i, vendor, product, serial); fprintf(stderr, " %d: %s, %s, SN: %s\n", i, vendor, product, serial); sprintf (userdev, "%08d", opts->rtl_dev_index); //check by index first, then by serial if (opts->rtl_dev_index == i) { fprintf (stderr, "Selected Device #%d with Serial Number: %s \n", i, serial); break; } else if (strcmp (userdev, serial) == 0) { fprintf (stderr, "Selected Device #%d with Serial Number: %s \n", i, serial); opts->rtl_dev_index = i; break; } } entry_win = newwin(18, WIDTH+20, starty+10, startx+10); box (entry_win, 0, 0); mvwprintw(entry_win, 2, 2, " Starting RTL Input. Cannot Release/Stop Until Exit."); mvwprintw(entry_win, 4, 2, " RTL Frequency: %d Hz", opts->rtlsdr_center_freq); mvwprintw(entry_win, 5, 2, " RTL Device Index Number: %d; SN:%s", opts->rtl_dev_index, serial); mvwprintw(entry_win, 6, 2, " RTL Device Bandwidth: %d kHz", opts->rtl_bandwidth); mvwprintw(entry_win, 7, 2, " RTL Device Gain: %d", opts->rtl_gain_value); mvwprintw(entry_win, 8, 2, " RTL Volume Multiplier: %d", opts->rtl_volume_multiplier); mvwprintw(entry_win, 9, 2, " RTL Device UDP Port: %d", opts->rtl_udp_port); mvwprintw(entry_win, 10, 2, " RTL Device PPM: %d", opts->rtlsdr_ppm_error); mvwprintw(entry_win, 11, 2, " RTL RMS Squelch: %d", opts->rtl_squelch_level); mvwprintw(entry_win, 13, 2, " Are You Sure?"); mvwprintw(entry_win, 14, 2, " 1 = Yes, 2 = No "); mvwprintw(entry_win, 15, 3, " "); echo(); refresh(); wscanw(entry_win, "%d", &confirm); noecho(); refresh(); //works well, but can't release dongle later, so its a one way trip until exit //TODO: Write function to release the dongle -- call cleanup_rtlsdr_stream() if (confirm == 1) { opts->audio_in_type = 3; //RTL input, only set this on confirm choicec = 18; //exit to decoder screen only if confirmed, otherwise, just go back } #else UNUSED(confirm); #endif } if (choicec == 7) //RTL DEV Retune { //read in new rtl frequency #ifdef USE_RTLSDR entry_win = newwin(6, WIDTH+18, starty+10, startx+10); box (entry_win, 0, 0); mvwprintw(entry_win, 2, 2, " Enter Frequency in Hz (851.8 MHz is 851800000 Hz) "); mvwprintw(entry_win, 3, 3, " "); echo(); refresh(); wscanw(entry_win, "%d", &opts->rtlsdr_center_freq); noecho(); temp_freq = opts->rtlsdr_center_freq; #endif choicec = 18; } if (choicec == 8) { if (state->rf_mod == 0) { state->rf_mod = 1; state->samplesPerSymbol = 8; state->symbolCenter = 3; opts->mod_c4fm = 0; opts->mod_qpsk = 1; } else { state->rf_mod = 0; state->samplesPerSymbol = 10; state->symbolCenter = 4; opts->mod_c4fm = 1; opts->mod_qpsk = 0; } } if (choicec == 9) { if (state->rf_mod == 0) { state->rf_mod = 1; state->samplesPerSymbol = 10; state->symbolCenter = 4; opts->mod_c4fm = 0; opts->mod_qpsk = 1; } else { state->rf_mod = 0; state->samplesPerSymbol = 10; state->symbolCenter = 4; opts->mod_c4fm = 1; opts->mod_qpsk = 0; } } // if (choicec == 9) // { // if (opts->audio_out == 0) // { // opts->audio_out = 1; // opts->audio_out_type = 0; // // state->audio_out_buf_p = 0; // // state->audio_out_buf_pR = 0; // state->audio_out_idx = 0; // state->audio_out_idx2 = 0; // state->audio_out_idxR = 0; // state->audio_out_idx2R = 0; // } // else // { // opts->audio_out = 0; // opts->audio_out_type = 9; // // state->audio_out_buf_p = 0; // // state->audio_out_buf_pR = 0; // state->audio_out_idx = 0; // state->audio_out_idx2 = 0; // state->audio_out_idxR = 0; // state->audio_out_idx2R = 0; // } // } //TCP Audio Input if (choicec == 10) { //read in tcp hostname sprintf (opts->tcp_hostname, "%s", "localhost"); opts->tcp_portno = 7355; entry_win = newwin(8, WIDTH+16, starty+10, startx+10); box (entry_win, 0, 0); mvwprintw(entry_win, 2, 2, " Enter TCP Direct Link Hostname:"); mvwprintw(entry_win, 3, 2, " (default is localhost)"); mvwprintw(entry_win, 4, 2, " "); mvwprintw(entry_win, 5, 3, " "); echo(); refresh(); wscanw(entry_win, "%s", opts->tcp_hostname); noecho(); //read in tcp port number entry_win = newwin(8, WIDTH+16, starty+10, startx+10); box (entry_win, 0, 0); mvwprintw(entry_win, 2, 2, " Enter TCP Direct Link Port Number:"); mvwprintw(entry_win, 3, 2, " (default is 7355)"); mvwprintw(entry_win, 4, 2, " "); mvwprintw(entry_win, 5, 3, " "); echo(); refresh(); wscanw(entry_win, "%d", &opts->tcp_portno); noecho(); opts->tcp_sockfd = Connect(opts->tcp_hostname, opts->tcp_portno); if (opts->tcp_sockfd == 0) goto TCP_END; //successful connection, continue; opts->audio_in_type = 8; opts->audio_in_file_info = calloc(1, sizeof(SF_INFO)); opts->audio_in_file_info->samplerate=opts->wav_sample_rate; opts->audio_in_file_info->channels=1; opts->audio_in_file_info->seekable=0; opts->audio_in_file_info->format=SF_FORMAT_RAW|SF_FORMAT_PCM_16|SF_ENDIAN_LITTLE; opts->tcp_file_in = sf_open_fd(opts->tcp_sockfd, SFM_READ, opts->audio_in_file_info, 0); if(opts->tcp_file_in == NULL) { fprintf(stderr, "Error, couldn't open TCP with libsndfile: %s\n", sf_strerror(NULL)); if (opts->audio_out_type == 0) { sprintf (opts->audio_in_dev, "%s", "pulse"); opts->audio_in_type = 0; } else opts->audio_in_type = 5; } TCP_END: ; //do nothing } //RIGCTL if (choicec == 11) { //read in tcp hostname sprintf (opts->rigctlhostname, "%s", "localhost"); opts->rigctlportno = 4532; entry_win = newwin(8, WIDTH+16, starty+10, startx+10); box (entry_win, 0, 0); mvwprintw(entry_win, 2, 2, " Enter RIGCTL Hostname:"); mvwprintw(entry_win, 3, 2, " (default is localhost)"); mvwprintw(entry_win, 4, 2, " "); mvwprintw(entry_win, 5, 3, " "); echo(); refresh(); wscanw(entry_win, "%s", opts->rigctlhostname); noecho(); //read in tcp port number entry_win = newwin(8, WIDTH+16, starty+10, startx+10); box (entry_win, 0, 0); mvwprintw(entry_win, 2, 2, " Enter RIGCTL Port Number:"); mvwprintw(entry_win, 3, 2, " (default is 4532)"); mvwprintw(entry_win, 4, 2, " "); mvwprintw(entry_win, 5, 3, " "); echo(); refresh(); wscanw(entry_win, "%d", &opts->rigctlportno); noecho(); opts->rigctl_sockfd = Connect(opts->rigctlhostname, opts->rigctlportno); if (opts->rigctl_sockfd != 0) opts->use_rigctl = 1; else opts->use_rigctl = 0; } // if (choicec == 10) // { // if (opts->ncurses_compact == 0) // { // opts->ncurses_compact = 1; // } // else opts->ncurses_compact = 0; // } // if (choicec == 11) // { // if (opts->ncurses_history == 0) // { // opts->ncurses_history = 1; // } // else opts->ncurses_history = 0; // } if (choicec == 12) { //TODO: Add Closing of RAW files as well? opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, &state->event_history_s[0]); opts->wav_out_fR = close_and_rename_wav_file(opts->wav_out_fR, opts->wav_out_fileR, opts->wav_out_dir, &state->event_history_s[1]); opts->wav_out_file[0] = 0; //Bugfix for decoded wav file display after disabling opts->wav_out_fileR[0] = 0; opts->dmr_stereo_wav = 0; } if (choicec == 13) //lucky number 13 for OP25 Symbol Capture Bin Files { //read in filename for symbol capture bin entry_win = newwin(6, WIDTH+16, starty+10, startx+10); box (entry_win, 0, 0); mvwprintw(entry_win, 2, 2, " Enter OP25 Symbol Capture Bin Filename"); mvwprintw(entry_win, 3, 3, " "); echo(); refresh(); wscanw(entry_win, "%s", opts->audio_in_dev); //&opts->audio_in_dev noecho(); //do the thing with the thing struct stat stat_buf; if (stat(opts->audio_in_dev, &stat_buf) != 0) { fprintf (stderr,"Error, couldn't open %s\n", opts->audio_in_dev); goto SKIP; } if (S_ISREG(stat_buf.st_mode)) { opts->symbolfile = fopen(opts->audio_in_dev, "r"); opts->audio_in_type = 4; //symbol capture bin files } SKIP: choicec = 18; } if (choicec == 14) //replay last file { struct stat stat_buf; if (stat(opts->audio_in_dev, &stat_buf) != 0) { fprintf (stderr,"Error, couldn't open %s\n", opts->audio_in_dev); goto SKIPR; } if (S_ISREG(stat_buf.st_mode)) { opts->symbolfile = fopen(opts->audio_in_dev, "r"); opts->audio_in_type = 4; //symbol capture bin files } SKIPR: choicec = 18; //exit } if (choicec == 15) //stop/close last file PLAYBACK { if (opts->symbolfile != NULL) { if (opts->audio_in_type == 4) //check first, or issuing a second fclose will crash the SOFTWARE { fclose(opts->symbolfile); //free(): double free detected in tcache 2 (this is a new one) happens when closing more than once } } if (opts->audio_out_type == 0) opts->audio_in_type = 0; //set after closing to prevent above crash condition else (opts->audio_in_type = 5); choicec = 18; //exit } if (choicec == 16) //stop/close last file RECORDING { if (opts->symbol_out_f) { closeSymbolOutFile (opts, state); sprintf (opts->audio_in_dev, "%s", opts->symbol_out_file); //swap output bin filename to input for quick replay } choicec = 18; //exit } //toggle call alert beep if (choicec == 17) { if (opts->call_alert == 0) { opts->call_alert = 1; } else opts->call_alert = 0; } if (choicec != 0 && choicec != 18 ) //return to last menu { //return choice = 0; choicec = 0; keypad(test_win, FALSE); keypad(menu_win, TRUE); delwin(test_win); print_menu(menu_win, highlight); //wrefresh(menu_win); break; } if (choicec == 18) //exit both menus { //exit choice = 1; choicec = 0; keypad(test_win, FALSE); keypad(menu_win, TRUE); delwin(test_win); print_menu(menu_win, highlight); break; } } clrtoeol(); //clear to end of line? refresh(); } //Key Entry if (choice == 14) { state->payload_keyid = 0; state->payload_keyidR = 0; short int option = 0; entry_win = newwin(16, WIDTH+6, starty+10, startx+10); box (entry_win, 0, 0); mvwprintw(entry_win, 2, 2, "Key Type Selection"); mvwprintw(entry_win, 3, 2, " "); mvwprintw(entry_win, 4, 2, "1 - Basic Privacy "); mvwprintw(entry_win, 5, 2, "2 - Hytera Privacy "); mvwprintw(entry_win, 6, 2, "3 - NXDN/dPMR Scrambler "); mvwprintw(entry_win, 7, 2, "4 - Force BP/Scr Key Priority "); mvwprintw(entry_win, 8, 3, "-------------------------------- "); mvwprintw(entry_win, 9, 2, "5 - RC4 or DES Key "); // mvwprintw(entry_win, 10, 2, "6 - Force DMR RC4 Priority/LE "); mvwprintw(entry_win, 10, 2, "6 - AES-128 or AES-256 Key "); mvwprintw(entry_win, 11, 3, "-------------------------------- "); mvwprintw(entry_win, 14, 3, " "); echo(); refresh(); wscanw(entry_win, "%hd", &option); //%d noecho(); opts->dmr_mute_encL = 0; opts->dmr_mute_encR = 0; if (option == 1) { state->K = 0; entry_win = newwin(6, WIDTH+6, starty+10, startx+10); box (entry_win, 0, 0); mvwprintw(entry_win, 2, 2, "Basic Privacy Key Number (DEC):"); mvwprintw(entry_win, 3, 3, " "); echo(); refresh(); wscanw(entry_win, "%lld", &state->K); noecho(); if (state->K > 255) state->K = 255; state->keyloader = 0; //turn off keyloader } if (option == 2) { state->K1 = 0; state->K2 = 0; state->K3 = 0; state->K4 = 0; state->H = 0; entry_win = newwin(7, WIDTH+8, starty+10, startx+10); box (entry_win, 0, 0); mvwprintw(entry_win, 2, 2, " Enter **tera Privacy Key Value (HEX) "); mvwprintw(entry_win, 3, 2, " 10 Char or First 16 for 32/64"); mvwprintw(entry_win, 4, 3, " "); echo(); refresh(); wscanw(entry_win, "%llX", &state->H); noecho(); state->K1 = state->H; entry_win = newwin(7, WIDTH+8, starty+10, startx+10); box (entry_win, 0, 0); mvwprintw(entry_win, 2, 2, " Enter **tera Privacy Key Value 2 (HEX) "); mvwprintw(entry_win, 3, 2, " Second 16 Chars or Zero"); mvwprintw(entry_win, 4, 3, " "); echo(); refresh(); wscanw(entry_win, "%llX", &state->K2); noecho(); entry_win = newwin(7, WIDTH+8, starty+10, startx+10); box (entry_win, 0, 0); mvwprintw(entry_win, 2, 2, " Enter **tera Privacy Key Value 3 (HEX) "); mvwprintw(entry_win, 3, 2, " Third 16 Chars or Zero"); mvwprintw(entry_win, 4, 3, " "); echo(); refresh(); wscanw(entry_win, "%llX", &state->K3); noecho(); entry_win = newwin(7, WIDTH+8, starty+10, startx+10); box (entry_win, 0, 0); mvwprintw(entry_win, 2, 2, " Enter **tera Privacy Key Value 4 (HEX) "); mvwprintw(entry_win, 3, 2, " Fourth 16 Chars or Zero"); mvwprintw(entry_win, 4, 3, " "); echo(); refresh(); wscanw(entry_win, "%llX", &state->K4); noecho(); state->keyloader = 0; //turn off keyloader } if (option == 3) { state->R = 0; entry_win = newwin(6, WIDTH+6, starty+10, startx+10); box (entry_win, 0, 0); mvwprintw(entry_win, 2, 2, "NXDN/dPMR Scrambler Key Value (DEC):"); mvwprintw(entry_win, 3, 3, " "); echo(); refresh(); wscanw(entry_win, "%lld", &state->R); noecho(); if (state->R > 0x7FFF) state->R = 0x7FFF; state->keyloader = 0; //turn off keyloader } //toggle enforcement of basic privacy key over enc bit set on traffic if (option == 4) { if (state->M == 1 || state->M == 0x21) { state->M = 0; } else state->M = 1; } if (option == 5) { state->R = 0; entry_win = newwin(6, WIDTH+6, starty+10, startx+10); box (entry_win, 0, 0); mvwprintw(entry_win, 2, 2, "RC4/DES Key Value (HEX):"); mvwprintw(entry_win, 3, 3, " "); echo(); refresh(); wscanw(entry_win, "%llX", &state->R); noecho(); //disable size check for DES keys // if (state->R > 0xFFFFFFFFFF) // { // state->R = 0xFFFFFFFFFF; // } state->RR = state->R; //assign for both slots state->keyloader = 0; //turn off keyloader } //toggle enforcement of rc4 key over missing pi header/le on DMR // if (option == 6) // { // if (state->M == 1 || state->M == 0x21) // { // state->M = 0; // } // else state->M = 0x21; // } //load AES keys if (option == 6) { state->K1 = 0; state->K2 = 0; state->K3 = 0; state->K4 = 0; state->H = 0; memset (state->A1, 0, sizeof(state->A1)); memset (state->A2, 0, sizeof(state->A2)); memset (state->A3, 0, sizeof(state->A3)); memset (state->A4, 0, sizeof(state->A4)); entry_win = newwin(7, WIDTH+8, starty+10, startx+10); box (entry_win, 0, 0); mvwprintw(entry_win, 2, 2, " Enter AES128/256 Key Value (HEX) "); mvwprintw(entry_win, 3, 2, " First 16 Chars or Zero"); mvwprintw(entry_win, 4, 3, " "); echo(); refresh(); wscanw(entry_win, "%llX", &state->K1); noecho(); entry_win = newwin(7, WIDTH+8, starty+10, startx+10); box (entry_win, 0, 0); // mvwprintw(entry_win, 2, 2, " Enter **tera Privacy Key Value 2 (HEX) "); mvwprintw(entry_win, 3, 2, " Second 16 Chars or Zero"); mvwprintw(entry_win, 4, 3, " "); echo(); refresh(); wscanw(entry_win, "%llX", &state->K2); noecho(); entry_win = newwin(7, WIDTH+8, starty+10, startx+10); box (entry_win, 0, 0); // mvwprintw(entry_win, 2, 2, " Enter **tera Privacy Key Value 3 (HEX) "); mvwprintw(entry_win, 3, 2, " Third 16 Chars or Zero"); mvwprintw(entry_win, 4, 3, " "); echo(); refresh(); wscanw(entry_win, "%llX", &state->K3); noecho(); entry_win = newwin(7, WIDTH+8, starty+10, startx+10); box (entry_win, 0, 0); // mvwprintw(entry_win, 2, 2, " Enter **tera Privacy Key Value 4 (HEX) "); mvwprintw(entry_win, 3, 2, " Fourth 16 Chars or Zero"); mvwprintw(entry_win, 4, 3, " "); echo(); refresh(); wscanw(entry_win, "%llX", &state->K4); noecho(); //load the AES keys into a seperate handler state->A1[0] = state->A1[1] = state->K1; state->A2[0] = state->A2[1] = state->K2; state->A3[0] = state->A3[1] = state->K3; state->A4[0] = state->A4[1] = state->K4; //zero out the K1-K4 state->K1 = 0; state->K2 = 0; state->K3 = 0; state->K4 = 0; state->keyloader = 0; //turn off keyloader //check to see if there is a value loaded or not if (state->A1[0] == 0 && state->A2[0] == 0 && state->A3[0] == 0 && state->A4[0] == 0) state->aes_key_loaded[0] = 0; else state->aes_key_loaded[0] = 1; //mirror state->aes_key_loaded[1] = state->aes_key_loaded[0]; } if (state->K == 0 && state->K1 == 0 && state->K2 == 0 && state->K3 == 0 && state->K4 == 0) { opts->dmr_mute_encL = 1; opts->dmr_mute_encR = 1; } break; } if (choice == 99) //UNUSED { //setup Auto parameters--default ones // resetState (state); //use sparingly, may cause memory leak state->samplesPerSymbol = 10; state->symbolCenter = 4; sprintf (opts->output_name, "Legacy Auto"); opts->dmr_mono = 0; opts->dmr_stereo = 0; //this value is the end user option state->dmr_stereo = 0; //this values toggles on and off depending on voice or data handling opts->pulse_digi_rate_out = 8000; opts->pulse_digi_out_channels = 1; opts->frame_dstar = 1; opts->frame_x2tdma = 1; opts->frame_p25p1 = 1; opts->frame_p25p2 = 0; opts->frame_nxdn48 = 0; opts->frame_nxdn96 = 0; opts->frame_dmr = 1; opts->frame_dpmr = 0; opts->frame_provoice = 0; opts->frame_ysf = 1; opts->frame_m17 = 0; opts->mod_c4fm = 1; opts->mod_qpsk = 0; opts->mod_gfsk = 0; state->rf_mod = 0; opts->unmute_encrypted_p25 = 0; break; } if (choice == 6) { //ProVoice Specifics // resetState (state); //use sparingly, may cause memory leak state->samplesPerSymbol = 5; state->symbolCenter = 2; sprintf (opts->output_name, "EDACS/PV"); opts->dmr_mono = 0; opts->dmr_stereo = 0; //this value is the end user option state->dmr_stereo = 0; //this values toggles on and off depending on voice or data handling opts->pulse_digi_rate_out = 8000; opts->pulse_digi_out_channels = 1; opts->frame_dstar = 0; opts->frame_x2tdma = 0; opts->frame_p25p1 = 0; opts->frame_p25p2 = 0; opts->frame_nxdn48 = 0; opts->frame_nxdn96 = 0; opts->frame_dmr = 0; opts->frame_dpmr = 0; opts->frame_provoice = 1; opts->frame_ysf = 0; opts->frame_m17 = 0; opts->mod_c4fm = 0; opts->mod_qpsk = 0; opts->mod_gfsk = 1; state->rf_mod = 2; break; } if (choice == 4) { //DSTAR // resetState (state); //use sparingly, may cause memory leak state->samplesPerSymbol = 10; state->symbolCenter = 4; sprintf (opts->output_name, "DSTAR"); opts->dmr_mono = 0; opts->dmr_stereo = 0; //this value is the end user option state->dmr_stereo = 0; //this values toggles on and off depending on voice or data handling opts->pulse_digi_rate_out = 8000; opts->pulse_digi_out_channels = 1; opts->frame_dstar = 1; opts->frame_x2tdma = 0; opts->frame_p25p1 = 0; opts->frame_p25p2 = 0; opts->frame_nxdn48 = 0; opts->frame_nxdn96 = 0; opts->frame_dmr = 0; opts->frame_dpmr = 0; opts->frame_provoice = 0; opts->frame_ysf = 0; opts->frame_m17 = 0; opts->mod_c4fm = 1; opts->mod_qpsk = 0; opts->mod_gfsk = 0; state->rf_mod = 0; // state->rf_mod = 2; opts->unmute_encrypted_p25 = 0; break; } if (choice == 3) //was 5, changed to 3 and also made it the TDMA class { //TDMA -- was P25p1 only // resetState (state); //use sparingly, may cause memory leak // opts->use_heuristics = 1; //Causes issues with Voice Wide if (opts->use_heuristics == 1) { initialize_p25_heuristics(&state->p25_heuristics); initialize_p25_heuristics(&state->inv_p25_heuristics); } opts->frame_p25p1 = 1; opts->frame_p25p2 = 1; opts->frame_dmr = 1; state->samplesPerSymbol = 10; state->symbolCenter = 4; state->rf_mod = 0; sprintf (opts->output_name, "TDMA"); opts->dmr_mono = 0; opts->dmr_stereo = 1; //this value is the end user option state->dmr_stereo = 0; //this values toggles on and off depending on voice or data handling opts->pulse_digi_rate_out = 8000; opts->pulse_digi_out_channels = 2; opts->frame_dstar = 0; opts->frame_x2tdma = 0; opts->frame_nxdn48 = 0; opts->frame_nxdn96 = 0; opts->frame_dpmr = 0; opts->frame_provoice = 0; opts->frame_ysf = 0; opts->frame_m17 = 0; opts->mod_c4fm = 1; opts->mod_qpsk = 0; opts->mod_gfsk = 0; opts->unmute_encrypted_p25 = 0; break; } if (choice == 2) { //AUTO Stereo P25 1, 2, and DMR // resetState (state); //use sparingly, seems to cause issue when switching back to other formats if (opts->use_heuristics == 1) { initialize_p25_heuristics(&state->p25_heuristics); initialize_p25_heuristics(&state->inv_p25_heuristics); } // opts->use_heuristics = 0; opts->frame_dmr = 1; state->samplesPerSymbol = 10; state->symbolCenter = 4; state->rf_mod = 0; sprintf (opts->output_name, "AUTO"); opts->dmr_mono = 0; opts->dmr_stereo = 1; //this value is the end user option state->dmr_stereo = 0; //this values toggles on and off depending on voice or data handling opts->pulse_digi_rate_out = 8000; opts->pulse_digi_out_channels = 2; opts->frame_dstar = 0; opts->frame_x2tdma = 0; opts->frame_p25p1 = 1; opts->frame_p25p2 = 1; opts->frame_nxdn48 = 0; opts->frame_nxdn96 = 0; opts->frame_dpmr = 0; opts->frame_provoice = 0; opts->frame_ysf = 1; opts->frame_m17 = 0; opts->mod_c4fm = 1; opts->mod_qpsk = 0; opts->mod_gfsk = 0; state->rf_mod = 0; break; } if (choice == 7) { // resetState (state); //use sparingly, seems to cause issue when switching back to other formats state->samplesPerSymbol = 8; state->symbolCenter = 3; sprintf (opts->output_name, "P25p2"); opts->dmr_mono = 0; opts->dmr_stereo = 1; //this value is the end user option state->dmr_stereo = 0; //this values toggles on and off depending on voice or data handling opts->pulse_digi_rate_out = 8000; opts->pulse_digi_out_channels = 2; opts->frame_dmr = 0; opts->frame_dstar = 0; opts->frame_x2tdma = 0; opts->frame_p25p1 = 0; opts->frame_p25p2 = 1; opts->frame_nxdn48 = 0; opts->frame_nxdn96 = 0; opts->frame_dpmr = 0; opts->frame_provoice = 0; opts->frame_ysf = 0; opts->frame_m17 = 0; opts->mod_c4fm = 1; opts->mod_qpsk = 0; opts->mod_gfsk = 0; state->rf_mod = 0; break; } if (choice == 8) { //dPMR // resetState (state); //use sparingly, may cause memory leak state->samplesPerSymbol = 20; state->symbolCenter = 10; sprintf (opts->output_name, "dPMR"); opts->dmr_mono = 0; opts->dmr_stereo = 0; //this value is the end user option state->dmr_stereo = 0; //this values toggles on and off depending on voice or data handling opts->pulse_digi_rate_out = 8000; opts->pulse_digi_out_channels = 1; opts->frame_dstar = 0; opts->frame_x2tdma = 0; opts->frame_p25p1 = 0; opts->frame_nxdn48 = 0; opts->frame_nxdn96 = 0; opts->frame_dmr = 0; opts->frame_dpmr = 1; opts->frame_provoice = 0; opts->frame_ysf = 0; opts->frame_m17 = 0; opts->mod_c4fm = 1; opts->mod_qpsk = 0; opts->mod_gfsk = 0; state->rf_mod = 0; //opts->unmute_encrypted_p25 = 0; break; } if (choice == 9) { //NXDN48 // resetState (state); //use sparingly, may cause memory leak opts->frame_nxdn48 = 1; state->samplesPerSymbol = 20; state->symbolCenter = 10; state->rf_mod = 0; sprintf (opts->output_name, "NXDN48"); opts->dmr_mono = 0; opts->dmr_stereo = 0; //this value is the end user option state->dmr_stereo = 0; //this values toggles on and off depending on voice or data handling opts->pulse_digi_rate_out = 8000; opts->pulse_digi_out_channels = 1; opts->frame_dstar = 0; opts->frame_x2tdma = 0; opts->frame_p25p1 = 0; opts->frame_nxdn48 = 1; opts->frame_nxdn96 = 0; opts->frame_dmr = 0; opts->frame_dpmr = 0; opts->frame_provoice = 0; opts->frame_ysf = 0; opts->frame_m17 = 0; opts->mod_c4fm = 1; //opts->unmute_encrypted_p25 = 0; // opts->mod_qpsk = 0; // opts->mod_gfsk = 0; // state->rf_mod = 0; } if (choice == 10) { //NXDN96 // resetState (state); //use sparingly, may cause memory leak state->samplesPerSymbol = 10; state->symbolCenter = 4; sprintf (opts->output_name, "NXDN96"); opts->dmr_mono = 0; opts->dmr_stereo = 0; //this value is the end user option state->dmr_stereo = 0; //this values toggles on and off depending on voice or data handling opts->pulse_digi_rate_out = 8000; opts->pulse_digi_out_channels = 1; opts->frame_dstar = 0; opts->frame_x2tdma = 0; opts->frame_p25p1 = 0; opts->frame_nxdn48 = 0; opts->frame_nxdn96 = 1; opts->frame_dmr = 0; opts->frame_dpmr = 0; opts->frame_provoice = 0; opts->frame_ysf = 0; opts->frame_m17 = 0; opts->mod_c4fm = 1; opts->mod_qpsk = 0; opts->mod_gfsk = 0; state->rf_mod = 0; //opts->unmute_encrypted_p25 = 0; } if (choice == 11) { //Decode DMR Stereo (was X2-TDMA) // resetState (state); //use sparingly, may cause memory leak state->samplesPerSymbol = 10; state->symbolCenter = 4; // sprintf (opts->output_name, "X2-TDMA"); sprintf (opts->output_name, "DMR"); opts->dmr_mono = 0; opts->dmr_stereo = 1; //this value is the end user option state->dmr_stereo = 0; //this values toggles on and off depending on voice or data handling opts->pulse_digi_rate_out = 8000; opts->pulse_digi_out_channels = 2; opts->frame_dstar = 0; opts->frame_x2tdma = 0; opts->frame_p25p1 = 0; opts->frame_nxdn48 = 0; opts->frame_nxdn96 = 0; opts->frame_dmr = 1; opts->frame_dpmr = 0; opts->frame_provoice = 0; opts->frame_ysf = 0; opts->frame_m17 = 0; opts->mod_c4fm = 1; opts->mod_qpsk = 0; opts->mod_gfsk = 0; state->rf_mod = 0; //opts->unmute_encrypted_p25 = 0; } if (choice == 12) { //Decode YSF Fusion // resetState (state); //use sparingly, may cause memory leak state->samplesPerSymbol = 10; state->symbolCenter = 4; sprintf (opts->output_name, "YSF"); opts->dmr_mono = 0; opts->dmr_stereo = 0; //this value is the end user option state->dmr_stereo = 0; //this values toggles on and off depending on voice or data handling opts->pulse_digi_rate_out = 8000; opts->pulse_digi_out_channels = 1; opts->frame_dstar = 0; opts->frame_x2tdma = 0; opts->frame_p25p1 = 0; opts->frame_nxdn48 = 0; opts->frame_nxdn96 = 0; opts->frame_dmr = 0; opts->frame_dpmr = 0; opts->frame_provoice = 0; opts->frame_ysf = 1; opts->frame_m17 = 0; opts->mod_c4fm = 1; opts->mod_qpsk = 0; opts->mod_gfsk = 0; state->rf_mod = 0; } if (choice == 5) //was 3 { //Decode M17 // resetState (state); //use sparingly, may cause memory leak state->samplesPerSymbol = 10; state->symbolCenter = 4; sprintf (opts->output_name, "M17"); opts->dmr_mono = 0; opts->dmr_stereo = 0; //this value is the end user option state->dmr_stereo = 0; //this values toggles on and off depending on voice or data handling opts->pulse_digi_rate_out = 8000; opts->pulse_digi_out_channels = 1; opts->frame_dstar = 0; opts->frame_x2tdma = 0; opts->frame_p25p1 = 0; opts->frame_nxdn48 = 0; opts->frame_nxdn96 = 0; opts->frame_dmr = 0; opts->frame_dpmr = 0; opts->frame_provoice = 0; opts->frame_ysf = 0; opts->frame_m17 = 1; opts->mod_c4fm = 1; opts->mod_qpsk = 0; opts->mod_gfsk = 0; state->rf_mod = 0; } if (choice == 13) { //Set all signal for inversion or uninversion if (opts->inverted_dmr == 0) { opts->inverted_dmr = 1; opts->inverted_dpmr = 1; opts->inverted_x2tdma = 1; opts->inverted_ysf = 1; opts->inverted_m17 = 1; } else { opts->inverted_dmr = 0; opts->inverted_dpmr = 0; opts->inverted_x2tdma = 0; opts->inverted_ysf = 0; opts->inverted_m17 = 0; } } if (choice == 15) //reset event history { //initialize event history items (0 to 255) for (uint8_t i = 0; i < 2; i++) init_event_history(&state->event_history_s[i], 0, 255); } if (choice == 16) //toggle payload printing { if (opts->payload == 0) { opts->payload = 1; fprintf(stderr, "Payload on\n"); } else { opts->payload = 0; fprintf(stderr, "Payload Off\n"); } } if (choice == 17) { //hardset P2 WACN, SYSID, and NAC entry_win = newwin(6, WIDTH+16, starty+10, startx+10); box (entry_win, 0, 0); mvwprintw(entry_win, 2, 2, " Enter Phase 2 WACN (HEX)"); mvwprintw(entry_win, 3, 3, " "); echo(); refresh(); wscanw(entry_win, "%llX", &state->p2_wacn); //%X if (state->p2_wacn > 0xFFFFF) { state->p2_wacn = 0xFFFFF; } noecho(); entry_win = newwin(6, WIDTH+16, starty+10, startx+10); box (entry_win, 0, 0); mvwprintw(entry_win, 2, 2, " Enter Phase 2 SYSID (HEX)"); mvwprintw(entry_win, 3, 3, " "); echo(); refresh(); wscanw(entry_win, "%llX", &state->p2_sysid); //%X if (state->p2_sysid > 0xFFF) { state->p2_sysid = 0xFFF; } noecho(); entry_win = newwin(6, WIDTH+16, starty+10, startx+10); box (entry_win, 0, 0); mvwprintw(entry_win, 2, 2, " Enter Phase 2 NAC/CC (HEX)"); mvwprintw(entry_win, 3, 3, " "); echo(); refresh(); wscanw(entry_win, "%llX", &state->p2_cc); //%X if (state->p2_cc > 0xFFF) { state->p2_cc = 0xFFF; } noecho(); //need handling to truncate larger than expected values //set our hardset flag to 1 if values inserted, else set to 0 so we can attempt to get them from TSBK/LCCH if (state->p2_wacn != 0 && state->p2_sysid != 0 && state->p2_cc != 0) { state->p2_hardset = 1; } else state->p2_hardset = 0; } if (choice == 19) { short int lrrpchoice = 0; entry_win = newwin(10, WIDTH+16, starty+10, startx+10); box (entry_win, 0, 0); mvwprintw(entry_win, 2, 2, " Enable or Disable LRRP Data File"); mvwprintw(entry_win, 3, 2, " 1 - ~/lrrp.txt (QGis)"); mvwprintw(entry_win, 4, 2, " 2 - ./DSDPlus.LRRP (LRRP.exe)"); mvwprintw(entry_win, 5, 2, " 3 - ./Custom Filename"); mvwprintw(entry_win, 6, 2, " 4 - Cancel/Stop"); mvwprintw(entry_win, 7, 2, " "); mvwprintw(entry_win, 8, 2, " "); echo(); refresh(); wscanw(entry_win, "%hd", &lrrpchoice); //%d noecho(); if (lrrpchoice == 1) { //find user home directory and append directory and filename. char * filename = "/lrrp.txt"; char * home_dir = getenv("HOME"); char * filepath = malloc(strlen(home_dir) + strlen(filename) + 1); strncpy (filepath, home_dir, strlen(home_dir) + 1); strncat (filepath, filename, strlen(filename) + 1); //assign home directory/filename to lrrp_out_file sprintf (opts->lrrp_out_file, "%s", filepath); //double check make sure this still works opts->lrrp_file_output = 1; } else if (lrrpchoice == 2) { sprintf (opts->lrrp_out_file, "DSDPlus.LRRP"); opts->lrrp_file_output = 1; } else if (lrrpchoice == 3) { //read in filename for symbol capture bin opts->lrrp_out_file[0] = 0; entry_win = newwin(6, WIDTH+16, starty+10, startx+10); box (entry_win, 0, 0); mvwprintw(entry_win, 2, 2, " Enter LRRP Data Filename"); mvwprintw(entry_win, 3, 3, " "); echo(); refresh(); wscanw(entry_win, "%s", opts->lrrp_out_file); //&opts->lrrp_out_file noecho(); if (opts->lrrp_out_file[0] != 0) //NULL { opts->lrrp_file_output = 1; } else { opts->lrrp_file_output = 0; sprintf (opts->lrrp_out_file, "%s", ""); opts->lrrp_out_file[0] = 0; } } else { opts->lrrp_file_output = 0; sprintf (opts->lrrp_out_file, "%s", ""); opts->lrrp_out_file[0] = 0; } } if (choice == 20) { exitflag = 1; break; } if(choice != 0 && choice != 20) /* User did a choice come out of the infinite loop */ break; } clrtoeol(); //clear to end of line? refresh(); state->menuopen = 0; //flag the menu is closed, resume processing getFrameSync //reopen pulse output with new parameters, if not null output type if (opts->audio_out == 1 && opts->audio_out_type == 0) { openPulseOutput (opts); } if (opts->audio_out_type == 2 || opts->audio_out_type == 5) { openOSSOutput (opts); } if (opts->audio_in_type == 0) //reopen pulse input if it is the specified input method { openPulseInput(opts); } //fix location of this statement to inside the if statement and add conditions if (opts->audio_in_type == 3) //open rtl input if it is the specified input method { // ncursesPrinter (opts, state); //not sure why this was placed here originally, but causes a double free core dump when calling free(timestr) #ifdef USE_RTLSDR if (opts->rtl_started == 0) { opts->rtl_started = 1; //set here so ncurses terminal doesn't attempt to open it again open_rtlsdr_stream(opts); } rtl_clean_queue(); reset_dibit_buffer(state); //test and observe for any random issues, disable if needed #elif AERO_BUILD opts->audio_out_type = 5; //hopefully the audio stream is still/already open //shouldn't this be 5? Was set to 3 #else opts->audio_out_type = 0; //need to see if we need to open pulseoutput as well here? openPulseOutput (opts); #endif } if (opts->audio_in_type == 8) //re-open TCP input 'file' { opts->tcp_file_in = sf_open_fd(opts->tcp_sockfd, SFM_READ, opts->audio_in_file_info, 0); } //update sync time on cc sync so we don't immediately go CC hunting when exiting the menu state->last_cc_sync_time = time(NULL); } //end Ncurses Menu void ncursesPrinter (dsd_opts * opts, dsd_state * state) { uint8_t idas = 0; int level = 0; int c = 0; //for filenames (no colons, etc) char * timestr = getTime(); char * datestr = getDate(); if (opts->audio_in_type != 1) //can't run getch/menu when using STDIN - { timeout(0); // c = getch(); // } //use rtl_dev_tune #ifdef USE_RTLSDR if (temp_freq == opts->rtlsdr_center_freq) { rtl_dev_tune (opts, temp_freq); temp_freq = -1; } #endif //Variable reset/set section //set lls sync types if (state->synctype >= 0) lls = state->synctype; //EDACS Channel Tree if ( (lls == 14 || lls == 15 || lls == 37 || lls == 38) && state->carrier == 1) { if (state->edacs_vc_lcn != -1) { edacs_channel_tree[state->edacs_vc_lcn][0] = lls; edacs_channel_tree[state->edacs_vc_lcn][1] = state->edacs_vc_lcn; edacs_channel_tree[state->edacs_vc_lcn][2] = state->lasttg; //EDACS standard does not provide source LIDs on channel update messages; instead, for the sake of display, let's //assume the prior source for a given LCN is still accurate, unless we have an updated one provided (or the call //type has changed under us). // //If you MUST have perfectly-accurate source LIDs, look at the logged CC messages yourself - incorrect source LIDs //may be displayed if we miss an initial call channel assignment. if (state->ea_mode == 1 || (state->lastsrc != 0 || edacs_channel_tree[state->edacs_vc_lcn][4] != state->edacs_vc_call_type)) edacs_channel_tree[state->edacs_vc_lcn][3] = state->lastsrc; edacs_channel_tree[state->edacs_vc_lcn][4] = state->edacs_vc_call_type; edacs_channel_tree[state->edacs_vc_lcn][5] = time(NULL); } } //Start Printing Section erase(); if (opts->ncurses_compact == 1) { printw ("------------------------------------------------------------------------------\n"); printw ("| Digital Speech Decoder: Florida Man Edition - AW %s \n", GIT_TAG); printw ("------------------------------------------------------------------------------\n"); } if (opts->ncurses_compact == 0) { attron(COLOR_PAIR(6)); for (short int i = 0; i < 7; i++) { printw("%s", FM_bannerN[i]); if (i == 1) printw (" ESC to Menu"); if (i == 2) printw (" 'q' to Quit "); if (i == 3 && state->debug_mode == 1) printw (" DEBUG MODE!"); if (i == 4 && opts->frame_m17 == 0) printw (" MBElib %s", versionstr); #ifdef USE_CODEC2 if (i == 4 && opts->frame_m17 == 1) printw (" CODEC2"); #endif #ifdef __CYGWIN__ if (i == 5) printw (" %s ", "AW (CYGWIN)"); #else if (i == 5) printw (" %s ", "AW "); #endif if (i == 6) printw (" %s \n", GIT_TAG); else printw ("\n"); } attroff(COLOR_PAIR(6)); //6 attron(COLOR_PAIR(4)); } //fix color/pair issue when compact and trunking enabled if (opts->ncurses_compact == 1 && opts->p25_trunk == 1) attron(COLOR_PAIR(4)); printw ("--Input Output----------------------------------------------------------------\n"); if (opts->audio_in_type == 0) { printw ("| Pulse Signal Input: %i kHz; %i Ch; ", opts->pulse_digi_rate_in/1000, opts->pulse_digi_in_channels); if (opts->pa_input_idx[0] != 0) printw (" D: %s;", opts->pa_input_idx); if (opts->use_rigctl == 1) printw ("RIG: %s:%d; ", opts->tcp_hostname, opts->rigctlportno); printw ("\n"); } if (opts->audio_in_type == 5) { printw ("| OSS Signal Input: %i kHz; 1 Ch;", SAMPLE_RATE_IN/1000); if (opts->use_rigctl == 1) printw ("RIG: %s:%d; ", opts->tcp_hostname, opts->rigctlportno); printw ("\n"); } if (opts->audio_in_type == 4) { printw ("| Dibit Bin Input: %s \n", opts->audio_in_dev); } if (opts->audio_in_type == 44) { printw ("| Symbol Float Input: %s \n", opts->audio_in_dev); } if (opts->m17decoderip == 1 && opts->udp_sockfd) printw ("| M17 UDP IP Frame Input: %s:%d \n", opts->m17_hostname, opts->m17_portno); if (opts->audio_in_type == 8) { printw ("| TCP Signal Input: %s:%d; %d kHz; 1 Ch; ", opts->tcp_hostname, opts->tcp_portno, opts->wav_sample_rate/1000); if (opts->use_rigctl == 1) printw ("RIG: %s:%d; ", opts->tcp_hostname, opts->rigctlportno); printw ("\n"); } if (opts->audio_in_type == 2) { printw ("| WAV Audio Input: %s; %d kHz; \n", opts->audio_in_dev, opts->wav_sample_rate); } if (opts->audio_in_type == 1) { printw ("| STDIN Standard Input: - Menu Disabled when using STDIN!\n"); // printw ("| NCURSES Menu Disabled when using STDIN! - Use CTRL + C to Close. \n"); } if (opts->audio_in_type == 3) { printw ("| RTL: %d;", opts->rtl_dev_index); if (opts->rtl_gain_value == 0) printw (" G: AGC;"); else printw (" G: %idB;", opts->rtl_gain_value); printw (" V: %iX;", opts->rtl_volume_multiplier); printw (" PPM: %i;", opts->rtlsdr_ppm_error); //Adjust manually now with { and } printw (" SQ: %i;", opts->rtl_squelch_level); printw (" RMS: %04li;", opts->rtl_rms); printw (" BW: %i;", opts->rtl_bandwidth); printw (" FRQ: %i;", opts->rtlsdr_center_freq); if (opts->rtl_udp_port != 0) printw ("\n| External RTL Tuning on UDP Port: %i", opts->rtl_udp_port); printw ("\n"); } if (opts->audio_out_type == 0) { printw ("| Pulse Digital Output: %i kHz; %i Ch; G: %02.0f%%", opts->pulse_digi_rate_out/1000, opts->pulse_digi_out_channels, state->aout_gain*2); if (opts->pulse_digi_out_channels == 2) printw (" G: %02.0f%%", state->aout_gainR*2); if (opts->floating_point == 1) printw (" FLOAT: %02.0f%%;", opts->audio_gain*2); if (opts->audio_gain == 0) printw (" (+|-) Auto "); if (opts->audio_gain > 0) printw (" (+|-) Manual"); if (opts->use_hpf_d == 1) printw (" HPF"); if (opts->call_alert == 1) printw (" *CA!"); //Call Alert if (opts->pa_output_idx[0] != 0) printw (" D: %s;", opts->pa_output_idx); printw (" \n"); } if ( opts->audio_out_type == 0 && (opts->frame_provoice == 1 || opts->monitor_input_audio == 1) ) { printw ("| Pulse Analog Output: %i kHz; %i Ch; G: %02.0f%% (/|*) ", opts->pulse_raw_rate_out/1000, opts->pulse_raw_out_channels, opts->audio_gainA); if (opts->audio_gainA == 0.0f) printw ("Auto "); else printw ("Manual "); if (opts->audio_in_type != 3) printw ("RMS: %04ld; ", opts->rtl_rms); if (opts->use_lpf == 1) printw ("F: |LP|"); else printw ("F: | |"); if (opts->use_hpf == 1) printw ("HP|"); else printw (" |"); if (opts->use_pbf == 1) printw ("PB|"); else printw (" |"); if (opts->pa_output_idx[0] != 0) printw (" D: %s;", opts->pa_output_idx); printw (" \n"); } if (opts->audio_out_type == 5 || opts->audio_out_type == 2) { printw ("| OSS Audio Output: %i kHz; %i Ch; G: %02.0f%%", opts->pulse_digi_rate_out/1000, opts->pulse_digi_out_channels, state->aout_gain*2); if (opts->pulse_digi_out_channels == 2) printw (" G: %02.0f%%", state->aout_gainR*2); if (opts->audio_gain == 0) printw (" (+/-) Auto "); if (opts->audio_gain > 0) printw (" (+/-) Manual"); if (opts->use_hpf_d == 1) printw (" HPF"); if (opts->call_alert == 1) printw (" *CA!"); //Call Alert if ( (opts->audio_out_type == 5 && opts->pulse_digi_rate_out == 48000 && opts->pulse_digi_out_channels == 1) && (opts->frame_provoice == 1 || opts->monitor_input_audio == 1) ) { printw ("\n| Analog Monitor RMS: %04ld; G: %02.0f%% (/|*) ", opts->rtl_rms, opts->audio_gainA); if (opts->audio_gainA == 0.0f) printw ("Auto "); else printw ("Manual "); if (opts->use_lpf == 1) printw ("F: |LP|"); else printw ("F: | |"); if (opts->use_hpf == 1) printw ("HP|"); else printw (" |"); if (opts->use_pbf == 1) printw ("PB|"); else printw (" |"); } printw (" \n"); } if (opts->audio_out_type == 8) { printw ("| UDP Digital Output: %s:%d; %d kHz %d Ch; %02.0f%%", opts->udp_hostname, opts->udp_portno, opts->pulse_digi_rate_out/1000, opts->pulse_digi_out_channels, state->aout_gain*2); if (opts->pulse_digi_out_channels == 2) printw (" G: %02.0f%%", state->aout_gainR*2); if (opts->audio_gain == 0) printw (" (+/-) Auto "); if (opts->audio_gain > 0) printw (" (+/-) Manual"); if (opts->use_hpf_d == 1) printw (" HPF"); if (opts->call_alert == 1) printw (" *CA!"); //Call Alert if ( (opts->audio_out_type == 5 && opts->pulse_digi_rate_out == 48000 && opts->pulse_digi_out_channels == 1) && (opts->frame_provoice == 1 || opts->monitor_input_audio == 1) ) printw (" - Monitor RMS: %04ld ", opts->rtl_rms); printw (" \n"); if (opts->udp_sockfdA != 0) //Analog Output on udp port +2 { printw ("| UDP Analog Output: %s:%d; 48 kHz 1 Ch; G: %02.0f%% (/|*) ", opts->udp_hostname, opts->udp_portno+2, opts->audio_gainA); if (opts->audio_gainA == 0.0f) printw ("A "); else printw ("M "); if (opts->audio_in_type != 3) printw ("RMS: %04ld; ", opts->rtl_rms); if (opts->use_lpf == 1) printw ("F: |LP|"); else printw ("F: | |"); if (opts->use_hpf == 1) printw ("HP|"); else printw (" |"); if (opts->use_pbf == 1) printw ("PB|"); else printw (" |"); printw (" \n"); } } if (opts->m17encoder == 1) { printw ("| M17 Encoder:"); if (state->m17encoder_tx == 1 && state->m17_vox == 0) printw (" Toggle TX (\\) ON ;"); if (state->m17encoder_tx == 0 && state->m17_vox == 0) printw (" Toggle TX (\\) OFF;"); if (state->m17_vox == 1) printw (" Vox Mode;"); printw (" Input Gain (/|*): %02.0f%% ", opts->audio_gainA); if (opts->use_lpf == 1) printw ("F: |LP|"); else printw ("F: | |"); if (opts->use_hpf == 1) printw ("HP|"); else printw (" |"); if (opts->use_pbf == 1) printw ("PB|"); else printw (" |"); if (opts->audio_in_type != 3 && state->m17_vox == 1) printw ( " SQL: %04ld : %04d;", opts->rtl_rms, opts->rtl_squelch_level); printw ("\n"); } if (opts->m17_use_ip == 1) printw ("| M17 UDP IP Frame Output: %s:%d \n", opts->m17_hostname, opts->m17_portno); if (opts->mbe_out_dir[0] != 0 && opts->dmr_stereo == 0) { printw ("| Writing MBE data files to directory %s\n", opts->mbe_out_dir); } if (opts->wav_out_raw) { printw ("| Raw Audio Output: %s\n", opts->wav_out_file_raw); } if (opts->symbol_out_f) //don't display when not actively capturing { printw ("| Symbol Bin Output: %s\n", opts->symbol_out_file); } if (opts->dmr_stereo_wav == 0 && opts->wav_out_file[0] != 0) { printw ("| Decoded WAV Output: %s\n", opts->wav_out_file); } //new Per Call setup would just be random names until closed, //so no need to show, and will be in the event history anyways if (opts->dmr_stereo_wav == 1) printw ("| Per Call Wav File Enabled to Directory: %s \n", opts->wav_out_dir); #ifdef PRETTY_COLORS if (opts->p25_trunk == 1 && (opts->use_rigctl == 1 || opts->audio_in_type == 3) ) { printw ("| Trunking -"); if (opts->trunk_tune_group_calls == 0) attron(COLOR_PAIR(2)); printw (" Group(g)"); attron(COLOR_PAIR(4)); if (opts->trunk_tune_private_calls == 0) attron(COLOR_PAIR(2)); printw (" Private(u)"); attron(COLOR_PAIR(4)); if (opts->trunk_tune_data_calls == 0) attron(COLOR_PAIR(2)); printw (" Data(d)"); attron(COLOR_PAIR(4)); if (opts->trunk_tune_enc_calls == 0) attron(COLOR_PAIR(2)); printw (" Encrypted(e)"); attron(COLOR_PAIR(4)); printw (" Calls"); if (opts->trunk_use_allow_list == 1) printw (" - White List Mode\n"); else printw (" - Black List Mode\n"); } #else //set on to UPPER CASE, off to lower case if (opts->p25_trunk == 1 && (opts->use_rigctl == 1 || opts->audio_in_type == 3) ) { printw ("| Trunking -"); if (opts->trunk_tune_group_calls == 0) printw (" group(g)"); else printw (" GROUP(g)"); if (opts->trunk_tune_private_calls == 0) printw (" private(u)"); else printw (" PRIVATE(u)"); if (opts->trunk_tune_data_calls == 0) printw (" data(d)"); else printw (" DATA(d)"); if (opts->trunk_tune_enc_calls == 0) printw (" encrypted(e)"); else printw (" ENCRYPTED(e)"); printw (" Calls"); if (opts->trunk_use_allow_list == 1) printw (" - White List Mode\n"); else printw (" - Black List Mode\n"); } #endif //print additional information for EDACS modes and toggles #ifdef PRETTY_COLORS if (opts->p25_trunk == 1 && opts->frame_provoice == 1) { printw ("| \\--EDACS -"); if (state->ea_mode == -1) { attron(COLOR_PAIR(2)); printw (" Standard/Network"); printw (" Extended Address"); attron(COLOR_PAIR(4)); } else if (state->ea_mode == 0) { printw (" Standard/Network"); attron(COLOR_PAIR(2)); printw (" Extended Address"); attron(COLOR_PAIR(4)); } else if (state->ea_mode == 1) { attron(COLOR_PAIR(2)); printw (" Standard/Network"); attron(COLOR_PAIR(4)); printw (" Extended Address"); } printw (" Mode (S);"); printw(" ESK: %02X", state->esk_mask); printw (" (A);"); if (state->ea_mode == 0) printw (" AFS: %d:%d:%d;", state->edacs_a_bits, state->edacs_f_bits, state->edacs_s_bits); //TG Hold on EDACS, if specified by user if (state->tg_hold != 0) { // Compute AFS for display purposes only int a = (state->tg_hold >> state->edacs_a_shift) & state->edacs_a_mask; int f = (state->tg_hold >> state->edacs_f_shift) & state->edacs_f_mask; int s = state->tg_hold & state->edacs_s_mask; if (state->ea_mode == 1) printw ("\n| \\TG HOLD: %d; ", state->tg_hold); else { char afs_str[8]; getAfsString(state, afs_str, a, f, s); printw ("\n| \\TG HOLD: %d [%s]; ", state->tg_hold, afs_str); } } attron(COLOR_PAIR(4)); printw ("\n"); } #else //set on to UPPER CASE, off to lower case if (opts->p25_trunk == 1 && opts->frame_provoice == 1) { printw ("| \\--EDACS -"); if (state->ea_mode == -1) { printw (" standard/network"); printw (" extended address"); } else if (state->ea_mode == 0) { printw (" STANDARD/NETWORK"); printw (" extended address"); } else if (state->ea_mode == 1) { printw (" standard/network"); printw (" EXTENDED ADDRESS"); } printw (" Mode (S);"); printw(" ESK: %02X", state->esk_mask); printw (" (A) Toggle; "); if (state->ea_mode == 0) printw (" AFS: %d:%d:%d;", state->edacs_a_bits, state->edacs_f_bits, state->edacs_s_bits); //TG Hold on EDACS, if specified by user if (state->tg_hold != 0) { // Compute AFS for display purposes only int a = (state->tg_hold >> state->edacs_a_shift) & state->edacs_a_mask; int f = (state->tg_hold >> state->edacs_f_shift) & state->edacs_f_mask; int s = state->tg_hold & state->edacs_s_mask; if (state->ea_mode == 1) printw ("\n| \\TG HOLD: %d; ", state->tg_hold); else { char afs_str[8]; getAfsString(state, afs_str, a, f, s); printw ("\n| \\TG HOLD: %d [%s]; ", state->tg_hold, afs_str); } } printw ("\n"); } #endif // if (opts->aggressive_framesync == 0) printw ("| Selective CRC ERR Bypass Enabled (RAS) \n"); if (state->M == 1) { if (state->R != 0) printw ("| Forcing Key Priority -- NXDN Sc Key: %05lld \n", state->R); if (state->K != 0) printw ("| Forcing Key Priority -- Moto BP Key: %03lld \n", state->K); if (state->K1 != 0) printw ("| Forcing Key Priority -- Hytera BP Key: %016llX \n", state->K1); if (state->K != 0 && state->K1 != 0) printw ("| Warning! Multiple DMR Key Types Loaded! \n"); //warning may not be required } if (state->M == 0x21) { if (state->R != 0) printw ("| Forcing Key Priority -- RC4 Key: %010llX \n", state->R); } if (state->M == 0x16) printw ("| Forcing Key Priority -- TYT 16-bit Key: %04llX \n", state->H); if (opts->scanner_mode == 1) { printw ("| Scan Mode: "); if (state->lcn_freq_roll != 0) printw (" Frequency: %.06lf Mhz", (double)state->trunk_lcn_freq[state->lcn_freq_roll-1]/1000000); printw (" Speed: %.02lf sec \n", opts->trunk_hangtime); //not sure values less than 1 make a difference, may be system/environment dependent } if (opts->reverse_mute == 1) printw ("| Reverse Mute - Muting Unencrypted Voice\n"); // if (opts->call_alert == 1) printw ("| Call Alert Tone Enabled\n"); printw ("------------------------------------------------------------------------------\n"); attroff(COLOR_PAIR(4)); if (state->carrier == 1) { attron(COLOR_PAIR(3)); level = (int) state->max / 164; //only update on carrier present if (opts->mod_qpsk == 1) level = (int) state->max / 328; //test values here if (opts->audio_in_type == 4) level = 50; //hard set when reading symbol bin files, otherwise, it will just be near zero if (level > 100) level = 100; //cap max at 100 to prevent it going over reset = 1; } else level = 0; printw ("--Audio Decode----------------------------------------------------------------\n"); printw ("| Demod/Rate: "); if (opts->mod_qpsk == 1) printw ("[QPSK]"); if (opts->mod_c4fm == 1) printw ("[C4FM]"); if (opts->mod_gfsk == 1) printw ("[GFSK]"); printw ( "[%d] \n", (48000*opts->wav_interpolator)/state->samplesPerSymbol); if (opts->m17encoder == 1) printw ("| Encoding: [%s] \n", opts->output_name); printw ("| Decoding: [%s] ", opts->output_name); if (opts->aggressive_framesync == 0) printw ("CRC/(RAS) "); //debug -- troubleshoot voice tuning after grant on DMR CC, subsequent grant may not tune because tuner isn't available if (opts->p25_trunk == 1 && opts->p25_is_tuned == 1) printw ("Tuner Locked "); if (opts->p25_trunk == 1 && opts->p25_is_tuned == 0) printw ("Tuner Available "); printw ("\n"); printw ("| In Level: [%02d%%] \n", level); if (opts->dmr_stereo == 0) { printw ("| Voice Error: [%X][%X]", state->errs&0xF, state->errs2&0xF); if (opts->slot1_on == 0) printw (" OFF"); if (opts->slot1_on == 1) printw (" ON"); printw ("\n"); } if (opts->dmr_stereo == 1) { printw ("| Voice Error: [%X][%X] Slot 1 (1)", state->errs&0xF, state->errs2&0xF); if (opts->slot1_on == 0) printw (" OFF"); if (opts->slot1_on == 1) printw (" ON"); if (opts->slot_preference == 0) printw (" *Preferred (3)"); printw ("\n"); printw ("| Voice Error: [%X][%X] Slot 2 (2)", state->errsR&0xF, state->errs2R&0xF); if (opts->slot2_on == 0) printw (" OFF"); if (opts->slot2_on == 1) printw (" ON"); if (opts->slot_preference == 1) printw (" *Preferred (3)"); printw ("\n"); } printw ("------------------------------------------------------------------------------\n"); printw ("--Call Info-------------------------------------------------------------------\n"); //DSTAR if (lls == 6 || lls == 7 || lls == 18 || lls == 19) { printw ("| %s ", SyncTypes[lls]); printw ("\n"); printw ("| RPT2: %s", state->dstar_rpt2); printw (" RPT1: %s", state->dstar_rpt1); printw ("\n"); printw ("| DEST: %s", state->dstar_dst); printw (" SRC: %s", state->dstar_src); printw ("\n"); printw ("| TEXT: %s", state->dstar_txt); printw ("\n"); printw ("| DATA: %s", state->dstar_gps); printw ("\n"); } //M17 if (lls == 8 || lls == 9 || lls == 16 || lls == 17) { printw ("| "); printw ("M17: "); //insert data type and frame information if (state->m17_str_dt == 0) printw("Reserved"); if (state->m17_str_dt == 1) printw("Data "); if (state->m17_str_dt == 2) printw("Voice (3200) "); if (state->m17_str_dt == 3) printw("Voice (1600) + Data"); printw ("\n"); printw ("| "); printw ("DST: "); if (state->m17_dst == 0xFFFFFFFFFFFF) printw("BROADCAST "); else if (state->m17_dst != 0 && state->m17_dst >= 0xEE6B28000000) printw("RESERVED (%012llx) ", state->m17_dst); else printw("%s", state->m17_dst_str); printw ("\n"); printw ("| "); printw ("SRC: "); if (state->m17_src != 0 && state->m17_src >= 0xEE6B28000000) printw("RESERVED (%012llx)", state->m17_src); else printw("%s", state->m17_src_str); printw ("\n"); printw ("| "); printw ("CAN: %02d ", state->m17_can); printw ("\n"); printw ("| "); //fill in any extra info, like Meta (IV, etc) if (state->m17_enc == 1) { printw (" Scrambler - Type: %d", state->m17_enc_st); } if (state->m17_enc == 2) { attron(COLOR_PAIR(1)); printw ("AES-CTR - IV: "); //display packed meta as IV for (i = 0; i < 16; i++) printw ("%02X", state->m17_meta[i]); } if (state->m17_enc == 3) { printw (" Reserved Enc - Type: %d", state->m17_enc_st); } printw ("\n"); } //YSF if (lls == 30 || lls == 31) { // printw ("\n"); printw ("| "); printw ("Fusion - "); //insert data type and frame information if (state->ysf_dt == 0) printw("V/D1 "); if (state->ysf_dt == 1) printw("DATA "); if (state->ysf_dt == 2) printw("V/D2 "); if (state->ysf_dt == 3) printw("VWFR "); printw (" "); if (state->ysf_cm == 0) printw("Group/CQ "); if (state->ysf_cm == 3) printw("Private "); if (state->ysf_cm == 1) printw("Radio ID "); if (state->ysf_cm == 2) printw("Reserved "); if (state->ysf_fi == 0) printw("HC "); if (state->ysf_fi == 1) printw("CC "); if (state->ysf_fi == 2) printw("TC "); if (state->ysf_fi == 3) printw("XX "); printw ("\n"); printw ("| "); //NOTE: In Radio ID Mode, the DST will be split on Destination and Source Radio ID values printw ("DST: %s ", state->ysf_tgt); printw ("SRC: %s ", state->ysf_src); printw ("\n"); printw ("| "); printw ("UPL: %s ", state->ysf_upl); printw ("DNL: %s ", state->ysf_dnl); printw ("\n"); printw ("| "); printw ("RM1: %s ", state->ysf_rm1); printw ("RM2: %s ", state->ysf_rm2); printw ("\n"); printw ("| "); printw ("RM3: %s ", state->ysf_rm3); printw ("RM4: %s ", state->ysf_rm4); //these texts can get pretty long and out of sorts, and lots of 0x20 spaces //just going to leave these to only be in the console output // printw ("\n"); // printw ("| "); // printw ("TXT: "); // for (i = 4; i < 8; i++) // { // for (int j = 0; j < 20; j++) // { // //no spaces and no asterisks // if (state->ysf_txt[i][j] != 0x2A) // printw ("%c", state->ysf_txt[i][j]); // } // // printw (" "); //just a single space between each 'block' // } printw ("\n"); } //NXDN if (lls == 28 || lls == 29) { if (strcmp (state->nxdn_location_category, "Type-D") == 0) idas = 1; if (opts->p25_trunk == 1) { printw ("| "); if (opts->p25_is_tuned == 0) { if (idas == 0) printw ("Monitoring RCCH Channel"); //Control Channel if (idas == 1) printw ("Monitoring RTCH2 Channel"); //Idas RTCH2 Channel if (state->p25_cc_freq != 0) { printw (" - Frequency: %.06lf Mhz ", (double)state->p25_cc_freq/1000000); } } else if (opts->p25_is_tuned == 1) { if (idas == 0) printw ("Monitoring RTCH Channel"); //Traffic Channel if (idas == 1) printw ("Monitoring RTCH2 Channel"); //Idas RTCH2 if (state->p25_vc_freq[0] != 0) { printw (" - Frequency: %.06lf Mhz ", (double)state->p25_vc_freq[0]/1000000); } } printw ("\n"); } printw ("| "); // #ifdef LIMAZULUTWEAKS if (idas == 0) printw ("NXDN - RAN: %02d; ", state->nxdn_last_ran); if (idas == 1) printw ("IDAS - Area: %02d; ", state->nxdn_last_ran); // #else // if (idas == 0) printw ("NXDN - RAN: [%02d] ", rn); // if (idas == 1) printw ("IDAS - Area: [%02d] ", rn); // #endif if (state->nxdn_location_site_code != 0) { printw ("Cat: %s; ", state->nxdn_location_category); printw ("Sys Code: %d; ", state->nxdn_location_sys_code); printw ("Site Code: %d; ", state->nxdn_location_site_code); } //if system supports Direct Frequency Assignment if (state->nxdn_rcn == 1) printw ("DFA "); printw ("\n"); printw ("| "); printw ("TGT: [%5d] ", state->nxdn_last_tg); printw ("SRC: [%5d] ", state->nxdn_last_rid); printw ("Alias: [%s]", state->generic_talker_alias[0]); //Group Name Labels from CSV import for (int k = 0; k < state->group_tally; k++) { if (state->group_array[k].groupNumber == state->nxdn_last_tg) { printw ("TG: "); attron(COLOR_PAIR(4)); printw (" [%s]", state->group_array[k].groupName); printw ("[%s] ", state->group_array[k].groupMode); } else if (state->group_array[k].groupNumber == state->nxdn_last_rid) { attron(COLOR_PAIR(4)); printw (" [%s]", state->group_array[k].groupName); } if (state->carrier == 1) { attron(COLOR_PAIR(3)); } } if (state->carrier == 1) { printw(" %s ", state->nxdn_call_type); } printw ("\n|"); if (state->nxdn_cipher_type > 0) { // printw (" ALG: %d Key ID: %02X ", state->nxdn_cipher_type, state->nxdn_key); printw (" ALG: %d Key ID: %02d ", state->nxdn_cipher_type, state->nxdn_key); } if (state->nxdn_cipher_type == 0x1 && state->carrier == 1) { if (state->R == 0) attron(COLOR_PAIR(2)); printw ("Scrambler "); if (state->R == 0) attroff(COLOR_PAIR(2)); attron(COLOR_PAIR(3)); if (state->R != 0) { attron(COLOR_PAIR(1)); printw ("Seed: %04llX ", state->payload_miN); printw ("Key: %05lld ", state->R ); attron(COLOR_PAIR(3)); } } if (state->nxdn_cipher_type == 0x2 && state->carrier == 1) { attron(COLOR_PAIR(1)); printw ("IV: %016llX ", state->payload_miN); attron(COLOR_PAIR(2)); printw ("DES1 "); if (state->R != 0) printw ("Key: %016llX ", state->R); attroff(COLOR_PAIR(2)); attron(COLOR_PAIR(3)); } if (state->nxdn_cipher_type == 0x3 && state->carrier == 1) { attron(COLOR_PAIR(1)); printw ("IV: %016llX ", state->payload_miN); attron(COLOR_PAIR(2)); printw ("AES-256 "); if (state->aes_key_loaded[0] == 1) printw ("KS: %016llX", state->A4[0]); attroff(COLOR_PAIR(2)); attron(COLOR_PAIR(3)); } if (state->nxdn_cipher_type > 0x3 && state->carrier == 1) { attron(COLOR_PAIR(2)); printw ("Unknown Encryption "); attroff(COLOR_PAIR(2)); attron(COLOR_PAIR(3)); } //Active Trunking Channels (NXDN and IDAS) if (1 == 1) //opts->p25_trunk { printw ("\n"); printw ("| "); //active channel display attron(COLOR_PAIR(4)); for (int i = 0; i < 16; i++) { if (state->active_channel[i] != 0) printw ("%s", state->active_channel[i]); } if (state->carrier == 1) attron(COLOR_PAIR(3)); else attroff(COLOR_PAIR(4)); } //TG Hold, if specified by user if (state->tg_hold != 0) printw ("TG HOLD: %d", state->tg_hold); printw("\n"); } //P25 and DMR BS/MS if ( lls == 0 || lls == 1 || lls == 12 || lls == 13 || lls == 10 || lls == 11 || lls == 32 || lls == 33 || lls == 34 || lls == 35 || lls == 36) { printw ("| "); if (lls > 1 && lls < 30) { printw ("DMR BS - DCC: %02i; ", state->dmr_color_code); // printw ("%s %s", state->dmr_branding, state->dmr_branding_sub); printw ("%s ", state->dmr_branding); printw ("%s", state->dmr_branding_sub); printw ("%s", state->dmr_site_parms); //site id, net id, etc if (state->dmr_rest_channel > 0) { printw ("Rest LSN: %02d; ", state->dmr_rest_channel); if (state->trunk_chan_map[state->dmr_rest_channel] != 0) { printw ("Freq: %.06lf Mhz", (double)state->trunk_chan_map[state->dmr_rest_channel]/1000000); } } else if (state->p25_cc_freq != 0) { printw ("Freq: %.06lf MHz", (double)state->p25_cc_freq/1000000); } } else if (lls == 32 || lls == 33 || lls == 34) { printw ("DMR MS - DCC: %02i; ", state->dmr_color_code); } else if (lls == 0 || lls == 1) //P1 { printw ("P25p1 - %05llX:%03llX:%03llX; RFSS: %lld SITE: %lld ", state->p2_wacn, state->p2_sysid, state->p2_cc, state->p2_rfssid, state->p2_siteid); if (state->p25_cc_freq != 0) { printw ("Freq: %.06lf MHz", (double)state->p25_cc_freq/1000000); } //load talker aliases here (Moto, Tait, Harris) for (int16_t i = 0; i < state->group_tally; i++) { if (state->group_array[i].groupNumber == state->lastsrc) //or state->lastsrc { sprintf (state->generic_talker_alias[0], "%s", state->group_array[i].groupName); break; } } } else if (lls == 35 || lls == 36) //P2 { printw ("P25p2 - %05llX:%03llX:%03llX; RFSS: %lld SITE: %lld ", state->p2_wacn, state->p2_sysid, state->p2_cc, state->p2_rfssid, state->p2_siteid); if (state->p2_wacn == 0 || state->p2_sysid == 0 || state->p2_cc == 0) { attron(COLOR_PAIR(2)); printw (" Phase 2 Missing Parameters "); attron(COLOR_PAIR(3)); } else if (state->p2_wacn == 0xFFFFF || state->p2_sysid == 0xFFF || state->p2_cc == 0xFFF) { attron(COLOR_PAIR(2)); printw (" Phase 2 Invalid Parameters "); attron(COLOR_PAIR(3)); } else { if (state->p25_cc_freq != 0) { printw ("Freq: %.06lf MHz", (double)state->p25_cc_freq/1000000); } } //load talker aliases here (Moto, Tait, Harris) for (int16_t i = 0; i < state->group_tally; i++) { if (state->group_array[i].groupNumber == state->lastsrc) { sprintf (state->generic_talker_alias[0], "%s", state->group_array[i].groupName); break; } } //load talker aliases here (Moto, Tait, Harris) for (int16_t i = 0; i < state->group_tally; i++) { if (state->group_array[i].groupNumber == state->lastsrcR) { sprintf (state->generic_talker_alias[1], "%s", state->group_array[i].groupName); break; } } } printw ("\n"); //Slot 1 [0] printw ("| SLOT 1 - "); if (state->dmrburstL < 16 && state->carrier == 1 && state->lasttg > 0 && state->lastsrc > 0) { attron(COLOR_PAIR(2)); } printw ("TGT: [%8i] SRC: [%8i] ", state->lasttg, state->lastsrc); if (state->dmrburstL != 16 && state->carrier == 1 && state->lasttg > 0 && state->lastsrc > 0) { attroff(COLOR_PAIR(2)); attron(COLOR_PAIR(3)); } printw ("%s | ", state->call_string[0]); printw ("%s ", DMRBusrtTypes[state->dmrburstL]); if (opts->slot_preference == 1 && opts->audio_out_type == 5 && opts->audio_out == 1 && ( state->dmrburstL == 16 || state->dmrburstL == 21) && (state->dmrburstR == 16 || state->dmrburstR == 21)) printw ("*M*"); printw ("\n"); printw ("| V XTRA | "); //10 spaces if(state->dmrburstL == 16 && state->payload_algid == 0 && state->dmr_so & 0x40) //4F or CF mask? & 0xCF currently //&& (state->dmr_so & 0xCF) == 0x40 { attron(COLOR_PAIR(5)); printw (" **BP** "); attroff(COLOR_PAIR(5)); attron(COLOR_PAIR(3)); } if(state->dmrburstL == 16 && state->payload_algid == 0 && state->K > 0 && state->dmr_fid == 0x10 && state->dmr_so & 0x40) { attron(COLOR_PAIR(1)); printw ("BP Key: %3lld ", state->K); attroff(COLOR_PAIR(1)); attron(COLOR_PAIR(3)); } if(state->dmrburstL == 16 && state->payload_algid == 0 && state->H > 0 && state->dmr_fid == 0x68 && state->dmr_so & 0x40) { attron(COLOR_PAIR(1)); printw ("Hytera BP Key: %010llX ", state->H); attroff(COLOR_PAIR(1)); attron(COLOR_PAIR(3)); } //ALG, KeyID, MI //was key_id if(state->dmrburstL == 16 && state->payload_algid > 0 && state->dmr_so & 0x40) { attron(COLOR_PAIR(1)); printw ("ALG: 0x%02X KEY: 0x%02X MI: 0x%08llX ", state->payload_algid, state->payload_keyid, state->payload_mi); attroff(COLOR_PAIR(1)); attron(COLOR_PAIR(3)); } //P25 FDMA/TDMA if(state->dmrburstL > 19 && state->payload_algid > 0 && state->payload_algid != 0x80) { attron(COLOR_PAIR(1)); printw ("ALG: 0x%02X KEY: 0x%04X MI: 0x%016llX ", state->payload_algid, state->payload_keyid, state->payload_miP); attroff(COLOR_PAIR(1)); attron(COLOR_PAIR(3)); } //Anytone 0x01 will never show here since its converted to 0x21 for handling if (state->payload_algid == 0xAA || state->payload_algid == 0x21 || state->payload_algid == 0x01) { attron(COLOR_PAIR(1)); printw("RC4 "); if (state->R != 0) { printw("Key: %010llX ", state->R); } attron(COLOR_PAIR(3)); } if (state->payload_algid == 0x81 || state->payload_algid == 0x22) { attron(COLOR_PAIR(1)); printw("DES1 "); if (state->R != 0) { printw("Key: %010llX ", state->R); } attron(COLOR_PAIR(3)); } if (state->payload_algid == 0x9F) { attron(COLOR_PAIR(1)); printw("DES-XL "); if (state->R != 0) { printw("Key: %010llX ", state->R); } attron(COLOR_PAIR(3)); } if (state->payload_algid == 0x82) { attron(COLOR_PAIR(1)); printw("DES2 "); attron(COLOR_PAIR(3)); } if (state->payload_algid == 0x83) { attron(COLOR_PAIR(1)); printw("DES3 "); attron(COLOR_PAIR(3)); } if (state->payload_algid == 0x89 || state->payload_algid == 0x24) { attron(COLOR_PAIR(1)); printw("AES-128 "); if (state->aes_key_loaded[0] != 0) { printw("KS: %016llX ", state->A2[0]); } attron(COLOR_PAIR(3)); } if (state->payload_algid == 0x84 || state->payload_algid == 0x25 || state->payload_algid == 0x05) { attron(COLOR_PAIR(1)); printw("AES-256 "); if (state->aes_key_loaded[0] != 0) { printw("KS: %016llX ", state->A4[0]); } attron(COLOR_PAIR(3)); } if (state->payload_algid == 0x02) { attron(COLOR_PAIR(1)); printw("Hytera Enhanced"); if (state->R != 0) printw(" Key: %010llX", state->R); attron(COLOR_PAIR(3)); } printw ("\n"); //printw ("| | "); //10 spaces printw ("| D XTRA | "); attron(COLOR_PAIR(4)); if(state->dmrburstL == 16 || state->dmrburstL == 21 || state->dmrburstL == 26 || state->dmrburstL == 27 || state->dmrburstL == 32) //only during call { attron(COLOR_PAIR(4)); //Embedded GPS (not LRRP) printw ("%s ", state->dmr_embedded_gps[0]); //Embedded Talker Alias String printw ("%s ", state->generic_talker_alias[0]); attroff(COLOR_PAIR(5)); if (state->carrier == 1) { attron(COLOR_PAIR(3)); } } //LRRP if(state->dmrburstL != 16) //only during data and no trunking { attron(COLOR_PAIR(4)); printw ("%s", state->dmr_lrrp_gps[0]); } //Group Name Labels from CSV import if (state->dmrburstL == 16 || state->dmrburstL > 19) { for (int k = 0; k < state->group_tally; k++) { if (state->group_array[k].groupNumber == state->lasttg) { attron(COLOR_PAIR(4)); printw (" [%s]", state->group_array[k].groupName); printw ("[%s] ", state->group_array[k].groupMode); if (state->carrier == 1) { attron(COLOR_PAIR(3)); } } } } if (state->carrier == 1) attron(COLOR_PAIR(3)); else attroff(COLOR_PAIR(4)); printw ("\n"); //Slot 2 [1] -- Also Including DMR MS now to keep the display more 'uniform' in nature // if (lls < 30 || lls == 35 || lls == 36) { printw ("| SLOT 2 - "); if (state->dmrburstR < 16 && state->carrier == 1 && state->lasttgR > 0 && state->lastsrcR > 0) { attron(COLOR_PAIR(2)); } printw ("TGT: [%8i] SRC: [%8i] ", state->lasttgR, state->lastsrcR); if (state->dmrburstR != 16 && state->carrier == 1 && state->lasttgR > 0 && state->lastsrcR > 0) { attroff(COLOR_PAIR(2)); attron(COLOR_PAIR(3)); } printw ("%s | ", state->call_string[1]); printw ("%s ", DMRBusrtTypes[state->dmrburstR]); if (opts->slot_preference == 0 && opts->audio_out_type == 5 && opts->audio_out == 1 && ( state->dmrburstL == 16 || state->dmrburstL == 21) && (state->dmrburstR == 16 || state->dmrburstR == 21) ) printw ("*M*"); printw ("\n"); printw ("| V XTRA | "); //10 spaces if(state->dmrburstR == 16 && state->payload_algidR == 0 && state->dmr_soR & 0x40) //4F or CF mask? { attron(COLOR_PAIR(5)); printw (" **BP** "); attroff(COLOR_PAIR(5)); attron(COLOR_PAIR(3)); } if(state->dmrburstR == 16 && state->payload_algidR == 0 && state->K > 0 && state->dmr_soR & 0x40 && state->dmr_fidR == 0x10) { attron(COLOR_PAIR(1)); printw ("BP Key: %3lld ", state->K); attroff(COLOR_PAIR(1)); attron(COLOR_PAIR(3)); } if(state->dmrburstR == 16 && state->payload_algidR == 0 && state->H > 0 && state->dmr_soR & 0x40 && state->dmr_fidR == 0x68) { attron(COLOR_PAIR(1)); printw ("Hytera BP Key: %010llX ", state->H); attroff(COLOR_PAIR(1)); attron(COLOR_PAIR(3)); } //ALG, KeyID, MI 2 //was keyidR if(state->dmrburstR == 16 && state->payload_algidR > 0 && state->dmr_soR & 0x40) { attron(COLOR_PAIR(1)); printw ("ALG: 0x%02X KEY: 0x%02X MI: 0x%08llX ", state->payload_algidR, state->payload_keyidR, state->payload_miR); attroff(COLOR_PAIR(1)); attron(COLOR_PAIR(3)); } //P25-P1 and P2 if(state->dmrburstR > 19 && state->payload_algidR > 0 && state->payload_algidR != 0x80) { attron(COLOR_PAIR(1)); printw ("ALG: 0x%02X KEY: 0x%04X MI: 0x%016llX ", state->payload_algidR, state->payload_keyidR, state->payload_miN); attroff(COLOR_PAIR(1)); attron(COLOR_PAIR(3)); } //Anytone 0x01 will never show here since its converted to 0x21 for handling if (state->payload_algidR == 0xAA || state->payload_algidR == 0x21 || state->payload_algidR == 0x01) { attron(COLOR_PAIR(1)); printw("RC4 "); if (state->RR != 0) { printw("Key: %010llX ", state->RR); } attron(COLOR_PAIR(3)); } if (state->payload_algidR == 0x81 || state->payload_algidR == 0x22) { attron(COLOR_PAIR(1)); printw("DES1 "); if (state->RR != 0) { printw("Key: %010llX ", state->RR); } attron(COLOR_PAIR(3)); } if (state->payload_algidR == 0x9F) { attron(COLOR_PAIR(1)); printw("DES-XL "); if (state->RR != 0) { printw("Key: %010llX ", state->RR); } attron(COLOR_PAIR(3)); } if (state->payload_algidR == 0x82) { attron(COLOR_PAIR(1)); printw("DES2 "); attron(COLOR_PAIR(3)); } if (state->payload_algidR == 0x83) { attron(COLOR_PAIR(1)); printw("DES3 "); attron(COLOR_PAIR(3)); } if (state->payload_algidR == 0x89 || state->payload_algidR == 0x24) { attron(COLOR_PAIR(1)); printw("AES-128 "); if (state->aes_key_loaded[1] != 0) { printw("KS: %016llX ", state->A2[1]); } attron(COLOR_PAIR(3)); } if (state->payload_algidR == 0x84 || state->payload_algidR == 0x25 || state->payload_algidR == 0x05) { attron(COLOR_PAIR(1)); printw("AES-256 "); if (state->aes_key_loaded[1] != 0) { printw("KS: %016llX ", state->A4[1]); } attron(COLOR_PAIR(3)); } if (state->payload_algidR == 0x02) { attron(COLOR_PAIR(1)); printw("Hytera Enhanced"); if (state->RR != 0) printw(" Key: %010llX", state->RR); attron(COLOR_PAIR(3)); } printw ("\n"); //printw ("| | "); printw ("| D XTRA | "); attron(COLOR_PAIR(4)); if(state->dmrburstR == 16 || state->dmrburstR == 21) //only during call { //Embedded GPS (not LRRP) attron(COLOR_PAIR(4)); printw ("%s ", state->dmr_embedded_gps[1]); //Embedded Talker Alias String printw ("%s ", state->generic_talker_alias[1]); attroff(COLOR_PAIR(5)); if (state->carrier == 1) { attron(COLOR_PAIR(3)); } } //LRRP if(state->dmrburstR != 16) //only during data and no trunking { attron(COLOR_PAIR(4)); printw ("%s", state->dmr_lrrp_gps[1]); } //Group Name Labels from CSV import if (state->dmrburstR == 16 || state->dmrburstR > 19) { for (int k = 0; k < state->group_tally; k++) { if (state->group_array[k].groupNumber == state->lasttgR) { attron(COLOR_PAIR(4)); printw (" [%s]", state->group_array[k].groupName); printw ("[%s] ", state->group_array[k].groupMode); } if (state->carrier == 1) { attron(COLOR_PAIR(3)); } } } if (state->carrier == 1) attron(COLOR_PAIR(3)); else attroff(COLOR_PAIR(4)); printw ("\n"); if (1 == 1) //opts->p25_trunk == 1 { printw ("| | "); //Active Trunking Channels //active channel display attron(COLOR_PAIR(4)); for (int i = 0; i < 31; i++) //up to 31 idas channels { if (state->active_channel[i] != 0) printw ("%s", state->active_channel[i]); } if (state->carrier == 1) attron(COLOR_PAIR(3)); else attroff(COLOR_PAIR(4)); printw ("\n"); } if (1 == 1) //opts->p25_trunk == 1 { printw ("| | "); //Currently Tuned Frequency // Tuned Frequency Display if (state->p25_vc_freq[0] != 0) { attron(COLOR_PAIR(4)); printw ("Frequency: %.06lf MHz ", (double)state->p25_vc_freq[0]/1000000); } //TG Hold, if specified by user if (state->tg_hold != 0) printw ("TG HOLD: %d", state->tg_hold); if (state->carrier == 1) attron(COLOR_PAIR(3)); else attroff(COLOR_PAIR(4)); printw ("\n"); } } // end if not MS } //end DMR BS Types //dPMR if (lls == 20 || lls == 21 || lls == 22 || lls == 23 ||lls == 24 || lls == 25 || lls == 26 || lls == 27) { printw ("| DCC: [%i] ", state->dpmr_color_code); printw ("TGT: [%s] SRC: [%s] ", state->dpmr_target_id, state->dpmr_caller_id); printw ("\n| "); if (state->dPMRVoiceFS2Frame.Version[0] == 3) { attron(COLOR_PAIR(2)); printw ("Scrambler "); attroff(COLOR_PAIR(2)); attron(COLOR_PAIR(3)); if (state->R != 0) { attron(COLOR_PAIR(1)); printw ("KEY VALUE: [%05lld] ", state->R ); //printw ("SEED: [%04llX]", state->payload_miN); attron(COLOR_PAIR(3)); } } printw ("\n"); } //EDACS and ProVoice if (lls == 14 || lls == 15 || lls == 37 || lls == 38) { attroff (COLOR_PAIR(3)); //colors off for EDACS if (state->edacs_site_id != 0) { if (opts->p25_is_tuned == 0) { printw ("| Monitoring CC - LCN [%02d]\n", state->edacs_cc_lcn); } else { printw ("| Monitoring VC - LCN [%02d]\n", state->edacs_tuned_lcn); //since we are tuned, keep updating the time so it doesn't disappear during call edacs_channel_tree[state->edacs_tuned_lcn][5] = time(NULL); } printw ("| SITE [%03lld][%02llX]", state->edacs_site_id, state->edacs_site_id); if (state->ea_mode == 1) { printw (" Extended Addressing"); } else printw (" Standard/Networked"); if (state->esk_mask == 0xA0) { printw (" w/ ESK"); } else printw (" w/o ESK"); printw ("\n"); } for (i = 1; i <= state->edacs_lcn_count; i++) { // Compute AFS for display purposes only int a = (edacs_channel_tree[i][2] >> state->edacs_a_shift) & state->edacs_a_mask; int f = (edacs_channel_tree[i][2] >> state->edacs_f_shift) & state->edacs_f_mask; int s = edacs_channel_tree[i][2] & state->edacs_s_mask; printw ("| - LCN [%02d][%010.06lf] MHz", i, (double)state->trunk_lcn_freq[i-1]/1000000); //print Control Channel on LCN line with the current Control Channel if ( (i) == state->edacs_cc_lcn) { attron (COLOR_PAIR(1)); //yellow printw (" Control Channel"); attroff (COLOR_PAIR(1)); } int print_call = 0; //print active calls on corresponding LCN line if ((i != state->edacs_cc_lcn) && time(NULL) - edacs_channel_tree[i][5] < 2) { print_call = 3; attron (COLOR_PAIR(3)); } //print dying or dead calls in red for x seconds longer else if ( (i != state->edacs_cc_lcn) && (time(NULL) - edacs_channel_tree[i][5] >= 2) && (time(NULL) - edacs_channel_tree[i][5] < 5) ) { print_call = 2; attron (COLOR_PAIR(2)); } if (print_call != 0) { if (state->ea_mode == 1) { // Voice call if ((edacs_channel_tree[i][4] & EDACS_IS_VOICE) != 0) { // Group call if ((edacs_channel_tree[i][4] & EDACS_IS_GROUP) != 0) printw (" TGT [%8lld] SRC [%8lld]", edacs_channel_tree[i][2], edacs_channel_tree[i][3] ); // I-Call else if ((edacs_channel_tree[i][4] & EDACS_IS_INDIVIDUAL) != 0) printw (" TGT [%8lld] SRC [%8lld] I-Call", edacs_channel_tree[i][2], edacs_channel_tree[i][3] ); // System all-call else if ((edacs_channel_tree[i][4] & EDACS_IS_ALL_CALL) != 0) printw (" TGT [ SYSTEM ] SRC [%8lld] All-Call", edacs_channel_tree[i][3] ); // Interconnect call else if ((edacs_channel_tree[i][4] & EDACS_IS_INTERCONNECT) != 0) printw (" TGT [ SYSTEM ] SRC [%8lld] Interconnect", edacs_channel_tree[i][3] ); // Test call else if ((edacs_channel_tree[i][4] & EDACS_IS_TEST_CALL) != 0) printw (" TGT [ SYSTEM ] SRC [ SYSTEM ] Test Call"); // Unknown call else printw (" Unknown call type" ); // Call flags if ((edacs_channel_tree[i][4] & EDACS_IS_TEST_CALL) != 0) {} else if ((edacs_channel_tree[i][4] & EDACS_IS_DIGITAL) == 0) printw (" [Ana]"); else printw (" [Dig]"); if ((edacs_channel_tree[i][4] & EDACS_IS_EMERGENCY) != 0) printw ("[EM]"); } else // Data call printw (" TGT [ DATA ] SRC [%8lld] Data", edacs_channel_tree[i][3] ); } else { // Voice call if ((edacs_channel_tree[i][4] & EDACS_IS_VOICE) != 0) { // Group call if ((edacs_channel_tree[i][4] & EDACS_IS_GROUP) != 0) { char afs_str[8]; getAfsString(state, afs_str, a, f, s); printw (" TGT [%6lld][%s] SRC [%5lld]", edacs_channel_tree[i][2], afs_str, edacs_channel_tree[i][3] ); } // I-Call else if ((edacs_channel_tree[i][4] & EDACS_IS_INDIVIDUAL) != 0) if (getAfsStringLength(state) == 6) printw (" TGT [%6lld][ UNIT ] SRC [%5lld] I-Call", edacs_channel_tree[i][2], edacs_channel_tree[i][3] ); else printw (" TGT [%6lld][ UNIT ] SRC [%5lld] I-Call", edacs_channel_tree[i][2], edacs_channel_tree[i][3] ); // System all-call else if ((edacs_channel_tree[i][4] & EDACS_IS_ALL_CALL) != 0) if (getAfsStringLength(state) == 6) printw (" TGT [ SYSTEM ] SRC [%5lld] All-Call", edacs_channel_tree[i][3] ); else printw (" TGT [ SYSTEM ] SRC [%5lld] All-Call", edacs_channel_tree[i][3] ); // Interconnect call else if ((edacs_channel_tree[i][4] & EDACS_IS_INTERCONNECT) != 0) if (getAfsStringLength(state) == 6) printw (" TGT [ SYSTEM ] SRC [%5lld] Interconnect", edacs_channel_tree[i][3] ); else printw (" TGT [ SYSTEM ] SRC [%5lld] Interconnect", edacs_channel_tree[i][3] ); // Test call else if ((edacs_channel_tree[i][4] & EDACS_IS_TEST_CALL) != 0) if (getAfsStringLength(state) == 6) printw (" TGT [ SYSTEM ] SRC [ SYS ] Test Call"); else printw (" TGT [ SYSTEM ] SRC [ SYS ] Test Call"); // Unknown call else printw (" Unknown call type" ); // Call flags if ((edacs_channel_tree[i][4] & EDACS_IS_TEST_CALL) != 0) {} else if ((edacs_channel_tree[i][4] & EDACS_IS_DIGITAL) == 0) printw (" [Ana]"); else printw (" [Dig]"); if ((edacs_channel_tree[i][4] & EDACS_IS_AGENCY_CALL) != 0) printw ("[A]"); if ((edacs_channel_tree[i][4] & EDACS_IS_FLEET_CALL) != 0) printw ("[F]"); if ((edacs_channel_tree[i][4] & EDACS_IS_EMERGENCY) != 0) printw ("[EM]"); } // Data call else if (getAfsStringLength(state) == 6) printw (" TGT [ DATA ] SRC [%5lld] Data", edacs_channel_tree[i][3] ); else printw (" TGT [ DATA ] SRC [%5lld] Data", edacs_channel_tree[i][3] ); } for (int k = 0; k < state->group_tally; k++) { if (state->group_array[k].groupNumber == edacs_channel_tree[i][2] && edacs_channel_tree[i][2] != 0) { printw (" [%s]", state->group_array[k].groupName); printw ("[%s]", state->group_array[k].groupMode); break; } else if (state->group_array[k].groupNumber == edacs_channel_tree[i][3] && edacs_channel_tree[i][3] != 0) { printw (" [%s]", state->group_array[k].groupName); printw ("[%s]", state->group_array[k].groupMode); break; } } if (print_call == 3) attroff (COLOR_PAIR(3)); else if (print_call == 2) attroff (COLOR_PAIR(2)); } if (i == state->edacs_tuned_lcn && opts->p25_is_tuned == 1) printw (" **T**"); //asterisk which lcn is tuned printw ("\n"); } if (state->carrier == 1) { attron (COLOR_PAIR(3)); } } //fence bottom printw ("------------------------------------------------------------------------------\n"); //colors off if (state->carrier == 1){ //same as above attroff(COLOR_PAIR(3)); } //only print event history if enabled if (opts->ncurses_history == 1) { attron(COLOR_PAIR(4)); //cyan for history printw ("--Latest Event History ([|])---Slot %d (\\)-------------------------------------\n", eh_slot+1); for (uint16_t i = (state->eh_index+1); i < (state->eh_index+11); i++) { // if ((i%255) == 0) continue; //0 is the event in progress before completion char text_string[2000]; sprintf (text_string, "%s", "BUMBLEBEETUNA"); uint8_t slot = eh_slot; if (strncmp(text_string, state->event_history_s[slot].Event_History_Items[i%255].event_string, 13) != 0) { memcpy(text_string, state->event_history_s[slot].Event_History_Items[i%255].event_string, 71*sizeof(char)); text_string[71] = 0; //terminate string printw ("| #%03d %s \n", i%255, text_string); } else printw ("| #%03d \n", i%255); //empty event, but since we can freely scroll now, keeps things uniform sprintf (text_string, "%s", "BUMBLEBEETUNA"); if (strncmp(text_string, state->event_history_s[slot].Event_History_Items[i%255].text_message, 13) != 0) printw ("| %s \n", state->event_history_s[slot].Event_History_Items[i%255].text_message); sprintf (text_string, "%s", "BUMBLEBEETUNA"); if (strncmp(text_string, state->event_history_s[slot].Event_History_Items[i%255].alias, 13) != 0) printw ("| Alias: %s \n", state->event_history_s[slot].Event_History_Items[i%255].alias); sprintf (text_string, "%s", "BUMBLEBEETUNA"); if (strncmp(text_string, state->event_history_s[slot].Event_History_Items[i%255].gps_s, 13) != 0) printw ("| GPS: %s \n", state->event_history_s[slot].Event_History_Items[i%255].gps_s); sprintf (text_string, "%s", "BUMBLEBEETUNA"); if (strncmp(text_string, state->event_history_s[slot].Event_History_Items[i%255].internal_str, 13) != 0) printw ("| DSD-FME: %s \n", state->event_history_s[slot].Event_History_Items[i%255].internal_str); } printw ("------------------------------------------------------------------------------\n"); attroff(COLOR_PAIR(4)); //cyan for history } refresh(); //keyboard shortcuts - codes same as ascii codes if (c == 27) //esc key, open menu { if (opts->m17encoder == 0) //don't allow menu if using M17 encoder ncursesMenu (opts, state); } //use k and l keys to test tg hold toggles on slots 1 and slots 2 if (c == 107) //'k' key, hold tg on slot 1 for trunking purposes, or toggle clear { if (state->tg_hold == 0) state->tg_hold = state->lasttg; else state->tg_hold = 0; if ( (opts->frame_nxdn48 == 1 || opts->frame_nxdn96 == 1) && (state->tg_hold == 0) ) state->tg_hold = state->nxdn_last_tg; else if (opts->frame_provoice == 1 && state->ea_mode == 0) state->tg_hold = state->lastsrc; } if (c == 108) //'l' key, hold tg on slot 2 for trunking purposes, or toggle clear { if (state->tg_hold == 0) state->tg_hold = state->lasttgR; else state->tg_hold = 0; } //toggling when 48k/1 OSS still has some lag -- needed to clear out the buffer when switching if (c == 49) // '1' key, toggle slot1 on { //switching, but want to control each seperately plz if (opts->slot1_on == 1) { opts->slot1_on = 0; if (opts->slot_preference == 0) opts->slot_preference = 2; // opts->slot_preference = 1; //slot 2 //clear any previously buffered audio state->audio_out_float_buf_p = state->audio_out_float_buf + 100; state->audio_out_buf_p = state->audio_out_buf + 100; memset (state->audio_out_float_buf, 0, 100 * sizeof (float)); memset (state->audio_out_buf, 0, 100 * sizeof (short)); state->audio_out_idx2 = 0; state->audio_out_idx = 0; } else if (opts->slot1_on == 0) { opts->slot1_on = 1; if (opts->audio_out_type == 5) //OSS 48k/1 { opts->slot_preference = 0; //slot 1 // opts->slot2_on = 0; //turn off slot 2 } } } if (c == 50) // '2' key, toggle slot2 on { //switching, but want to control each seperately plz if (opts->slot2_on == 1) { opts->slot2_on = 0; opts->slot_preference = 0; //slot 1 //clear any previously buffered audio state->audio_out_float_buf_pR = state->audio_out_float_bufR + 100; state->audio_out_buf_pR = state->audio_out_bufR + 100; memset (state->audio_out_float_bufR, 0, 100 * sizeof (float)); memset (state->audio_out_bufR, 0, 100 * sizeof (short)); state->audio_out_idx2R = 0; state->audio_out_idxR = 0; } else if (opts->slot2_on == 0) { opts->slot2_on = 1; if (opts->audio_out_type == 5) //OSS 48k/1 { opts->slot_preference = 1; //slot 2 // opts->slot1_on = 0; //turn off slot 1 } } } // if (c == 51) //'3' key, toggle slot preference on 48k/1 // { // if (opts->slot_preference == 1) opts->slot_preference = 0; // else opts->slot_preference = 1; // } if (c == 51) //'3' key, cycle slot preference { if (opts->slot_preference == 0 || opts->slot_preference == 1) opts->slot_preference++; else opts->slot_preference = 0; } if (c == 43) //+ key, increment audio_gain { if (opts->audio_gain < 50) opts->audio_gain++; state->aout_gain = opts->audio_gain; state->aout_gainR = opts->audio_gain; opts->audio_gainR = opts->audio_gain; } if (c == 45) //- key, decrement audio_gain { if (opts->audio_gain > 0) opts->audio_gain--; state->aout_gain = opts->audio_gain; state->aout_gainR = opts->audio_gain; //reset to default on 0 for auto if (opts->audio_gain == 0) { state->aout_gain = 25; state->aout_gainR = 25; } opts->audio_gainR = opts->audio_gain; } if (c == 42) // * key, increment audio_gainA { if (opts->audio_gainA < 100) opts->audio_gainA++; } if (c == 47) // / key, decrement audio_gainA { if (opts->audio_gainA > 0) opts->audio_gainA--; } if (c == 122) //'z' key, toggle payload to console { if (opts->payload == 1) opts->payload = 0; else opts->payload = 1; } if (c == 99) //'c' key, toggle compact mode { if (opts->ncurses_compact == 1) opts->ncurses_compact = 0; else opts->ncurses_compact = 1; } if (c == 116) //'t' key, toggle trunking { if (opts->p25_trunk == 1) opts->p25_trunk = 0; else opts->p25_trunk = 1; } if (c == 121) //'y' key, toggle scanner mode { if (opts->scanner_mode == 1) opts->scanner_mode = 0; else opts->scanner_mode = 1; opts->p25_trunk = 0; //turn off trunking mode } if (c == 97) //'a' key, toggle call alert beep { if (opts->call_alert == 1) opts->call_alert = 0; else opts->call_alert = 1; } if (c == 104) //'h' key, toggle history { if (opts->ncurses_history == 1) opts->ncurses_history = 0; else opts->ncurses_history = 1; } if (c == 113) //'q' key, quit { exitflag = 1; } if (c == 52) // '4' key, toggle force privacy key over fid and svc (dmr) { if (state->M == 1 || state->M == 0x21) state->M = 0; else state->M = 1; } if (c == 54) // '6' key, toggle force rc4 key over missing pi header/late entry { if (state->M == 1 || state->M == 0x21) state->M = 0; else state->M = 0x21; } if (c == 105) //'i' key, toggle signal inversion on inverted types { //Set all signal for inversion or uninversion if (opts->inverted_dmr == 0) { opts->inverted_dmr = 1; opts->inverted_dpmr = 1; opts->inverted_x2tdma = 1; opts->inverted_ysf = 1; opts->inverted_m17 = 1; } else { opts->inverted_dmr = 0; opts->inverted_dpmr = 0; opts->inverted_x2tdma = 0; opts->inverted_ysf = 0; opts->inverted_m17 = 0; } } if (c == 109) //'m' key, toggle qpsk/c4fm - everything but phase 2 { if (state->rf_mod == 0) { opts->mod_c4fm = 0; opts->mod_qpsk = 1; opts->mod_gfsk = 0; state->rf_mod = 1; state->samplesPerSymbol = 10; state->symbolCenter = 4; } else { opts->mod_c4fm = 1; opts->mod_qpsk = 0; opts->mod_gfsk = 0; state->rf_mod = 0; state->samplesPerSymbol = 10; state->symbolCenter = 4; } } if (c == 77) //'M' key, toggle qpsk - phase 2 6000 sps { if (state->rf_mod == 0) { opts->mod_c4fm = 0; opts->mod_qpsk = 1; opts->mod_gfsk = 0; state->rf_mod = 1; state->samplesPerSymbol = 8; state->symbolCenter = 3; } else { opts->mod_c4fm = 1; opts->mod_qpsk = 0; opts->mod_gfsk = 0; state->rf_mod = 0; state->samplesPerSymbol = 8; state->symbolCenter = 3; } } if (c == 82) //'R', save symbol capture bin with date/time string as name { sprintf (opts->symbol_out_file, "%s_%s_dibit_capture.bin", datestr, timestr); openSymbolOutFile (opts, state); } if (c == 114) //'r' key, stop capturing symbol capture bin file { if (opts->symbol_out_f) { closeSymbolOutFile (opts, state); sprintf (opts->audio_in_dev, "%s", opts->symbol_out_file); } } #ifdef __CYGWIN__ //do nothing #else if (c == 32) //'space bar' replay last bin file (rework to do wav files too?) { struct stat stat_buf; if (stat(opts->audio_in_dev, &stat_buf) != 0) { fprintf (stderr,"Error, couldn't open %s\n", opts->audio_in_dev); goto SKIPR; } if (S_ISREG(stat_buf.st_mode)) { opts->symbolfile = fopen(opts->audio_in_dev, "r"); opts->audio_in_type = 4; //symbol capture bin files } SKIPR: ; //do nothing } #endif if (c == 80) //'P' key - start per call wav files //TODO: Fix { char wav_file_directory[1024]; sprintf (wav_file_directory, "%s", opts->wav_out_dir); wav_file_directory[1023] = '\0'; if (stat(wav_file_directory, &st_wav) == -1) { fprintf (stderr, "%s wav file directory does not exist\n", wav_file_directory); fprintf (stderr, "Creating directory %s to save decoded wav files\n", wav_file_directory); mkdir(wav_file_directory, 0700); } fprintf (stderr,"\n Per Call Wav File Enabled to Directory: %s;.\n", opts->wav_out_dir); srand(time(NULL)); //seed random for filenames (so two filenames aren't the exact same datetime string on initailization) opts->wav_out_f = open_wav_file(opts->wav_out_dir, opts->wav_out_file, 8000, 0); opts->wav_out_fR = open_wav_file(opts->wav_out_dir, opts->wav_out_fileR, 8000, 0); opts->dmr_stereo_wav = 1; } if (c == 112) //'p' key - stop all per call wav files //TODO: Fix { //TODO: Add Closing of RAW files as well? opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, &state->event_history_s[0]); opts->wav_out_fR = close_and_rename_wav_file(opts->wav_out_fR, opts->wav_out_fileR, opts->wav_out_dir, &state->event_history_s[1]); opts->wav_out_file[0] = 0; //Bugfix for decoded wav file display after disabling opts->wav_out_fileR[0] = 0; opts->dmr_stereo_wav = 0; } // #ifdef __CYGWIN__ //this might be okay on Aero as well, will need to look into and/or test // #else if (c == 115) //'s' key, stop playing wav or symbol in files { if (opts->symbolfile != NULL) { if (opts->audio_in_type == 4) { fclose(opts->symbolfile); } } if (opts->audio_in_type == 2) //wav input file { sf_close(opts->audio_in_file); } if (opts->audio_out_type == 0) { opts->audio_in_type = 0; openPulseInput(opts); } else opts->audio_in_type = 5; //exitflag = 1; } #endif //makes buzzing sound when locked out in new audio config and short, probably something to do with processaudio running or not running if (state->lasttg != 0 && opts->frame_provoice != 1 && c == 33) //SHIFT+'1' key (exclamation point), lockout slot 1 or conventional tg from tuning/playback during session { state->group_array[state->group_tally].groupNumber = state->lasttg; sprintf (state->group_array[state->group_tally].groupMode, "%s", "B"); sprintf (state->group_array[state->group_tally].groupName, "%s", "LOCKOUT"); state->group_tally++; sprintf (state->event_history_s[0].Event_History_Items[0].internal_str, "Target: %d; has been locked out; User Lock Out.", state->lasttg); watchdog_event_current(opts, state, 0); sprintf (state->call_string[0], "%s", " "); //21 spaces //if we have an opened group file, let's write a group lock out into it to make it permanent if (opts->group_in_file[0] != 0) //file is available { FILE * pFile; //file pointer //open file by name that is supplied in the ncurses terminal, or cli pFile = fopen (opts->group_in_file, "a"); fprintf (pFile, "%d,B,LOCKOUT,%02X\n", state->lasttg, state->payload_algid); fclose (pFile); } //extra safeguards due to sync issues with NXDN memset (state->nxdn_sacch_frame_segment, 1, sizeof(state->nxdn_sacch_frame_segment)); memset (state->nxdn_sacch_frame_segcrc, 1, sizeof(state->nxdn_sacch_frame_segcrc)); memset (state->active_channel, 0, sizeof(state->active_channel)); //reset dmr blocks dmr_reset_blocks (opts, state); //zero out additional items state->lasttg = 0; state->lasttgR = 0; state->lastsrc = 0; state->lastsrcR = 0; state->payload_algid = 0; state->payload_algidR = 0; state->payload_keyid = 0; state->payload_keyidR = 0; state->payload_mi = 0; state->payload_miR = 0; state->payload_miP = 0; state->payload_miN = 0; opts->p25_is_tuned = 0; state->p25_vc_freq[0] = state->p25_vc_freq[1] = 0; //tune back to the control channel -- NOTE: Doesn't work correctly on EDACS Analog Voice //RIGCTL if (opts->p25_trunk == 1 && opts->use_rigctl == 1) { //drop all items (failsafe) noCarrier(opts, state); if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw); SetFreq(opts->rigctl_sockfd, state->p25_cc_freq); } //rtl #ifdef USE_RTLSDR if (opts->p25_trunk == 1 && opts->audio_in_type == 3) { //drop all items (failsafe) noCarrier(opts, state); rtl_dev_tune (opts, state->p25_cc_freq); } #endif state->last_cc_sync_time = time(NULL); //if P25p2 VCH and going back to P25p1 CC, flip symbolrate if (state->p25_cc_is_tdma == 0) { state->samplesPerSymbol = 10; state->symbolCenter = 4; } } if (state->lasttgR != 0 && opts->frame_provoice != 1 && c == 64) //SHIFT+'2' key (@ at sign), lockout slot 2 tdma tgR from tuning/playback during session { state->group_array[state->group_tally].groupNumber = state->lasttgR; sprintf (state->group_array[state->group_tally].groupMode, "%s", "B"); sprintf (state->group_array[state->group_tally].groupName, "%s", "LOCKOUT"); state->group_tally++; sprintf (state->event_history_s[1].Event_History_Items[0].internal_str, "Target: %d; has been locked out; User Lock Out.", state->lasttgR); watchdog_event_current(opts, state, 1); sprintf (state->call_string[1], "%s", " "); //21 spaces //if we have an opened group file, let's write a group lock out into it to make it permanent if (opts->group_in_file[0] != 0) //file is available { FILE * pFile; //file pointer //open file by name that is supplied in the ncurses terminal, or cli pFile = fopen (opts->group_in_file, "a"); fprintf (pFile, "%d,B,LOCKOUT,%02X\n", state->lasttgR, state->payload_algidR); fclose (pFile); } //extra safeguards due to sync issues with NXDN memset (state->nxdn_sacch_frame_segment, 1, sizeof(state->nxdn_sacch_frame_segment)); memset (state->nxdn_sacch_frame_segcrc, 1, sizeof(state->nxdn_sacch_frame_segcrc)); memset (state->active_channel, 0, sizeof(state->active_channel)); //reset dmr blocks dmr_reset_blocks (opts, state); //zero out additional items state->lasttg = 0; state->lasttgR = 0; state->lastsrc = 0; state->lastsrcR = 0; state->payload_algid = 0; state->payload_algidR = 0; state->payload_keyid = 0; state->payload_keyidR = 0; state->payload_mi = 0; state->payload_miR = 0; state->payload_miP = 0; state->payload_miN = 0; opts->p25_is_tuned = 0; state->p25_vc_freq[0] = state->p25_vc_freq[1] = 0; //tune back to the control channel -- NOTE: Doesn't work correctly on EDACS Analog Voice //RIGCTL if (opts->p25_trunk == 1 && opts->use_rigctl == 1) { //drop all items (failsafe) noCarrier(opts, state); if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw); SetFreq(opts->rigctl_sockfd, state->p25_cc_freq); } //rtl #ifdef USE_RTLSDR if (opts->p25_trunk == 1 && opts->audio_in_type == 3) { //drop all items (failsafe) noCarrier(opts, state); rtl_dev_tune (opts, state->p25_cc_freq); } #endif state->last_cc_sync_time = time(NULL); //if P25p2 VCH and going back to P25p1 CC, flip symbolrate if (state->p25_cc_is_tdma == 0) { state->samplesPerSymbol = 10; state->symbolCenter = 4; } } if (opts->p25_trunk == 1 && c == 119) //'w' key, toggle white list/black list mode { if (opts->trunk_use_allow_list == 1) opts->trunk_use_allow_list = 0; else opts->trunk_use_allow_list = 1; } if (opts->p25_trunk == 1 && c == 117) //'u' key, toggle tune private calls { if (opts->trunk_tune_private_calls == 1) opts->trunk_tune_private_calls = 0; else opts->trunk_tune_private_calls = 1; } if (opts->p25_trunk == 1 && c == 100) //'d' key, toggle tune data calls { if (opts->trunk_tune_data_calls == 1) opts->trunk_tune_data_calls = 0; else opts->trunk_tune_data_calls = 1; } if (opts->p25_trunk == 1 && c == 101) //'e' key, toggle tune enc calls (P25 only on certain grants) { if (opts->trunk_tune_enc_calls == 1) opts->trunk_tune_enc_calls = 0; else opts->trunk_tune_enc_calls = 1; } if (opts->p25_trunk == 1 && c == 103) //'g' key, toggle tune group calls { if (opts->trunk_tune_group_calls == 1) opts->trunk_tune_group_calls = 0; else opts->trunk_tune_group_calls = 1; } if (c == 70) //'F' key - toggle agressive sync/crc failure/ras { if (opts->aggressive_framesync == 0) opts->aggressive_framesync = 1; else opts->aggressive_framesync = 0; } if (c == 68) //'D' key - Reset DMR Site Parms/Call Strings, etc. { //dmr trunking/ncurses stuff state->dmr_rest_channel = -1; //init on -1 state->dmr_mfid = -1; // //dmr mfid branding and site parms sprintf(state->dmr_branding_sub, "%s", ""); sprintf(state->dmr_branding, "%s", ""); sprintf (state->dmr_site_parms, "%s", ""); //DMR Location Area - DMRLA B***S*** opts->dmr_dmrla_is_set = 0; opts->dmr_dmrla_n = 0; //reset NXDN info state->nxdn_location_site_code = 0; state->nxdn_location_sys_code = 0; sprintf (state->nxdn_location_category, "%s", " "); state->nxdn_last_ran = -1; //0 state->nxdn_ran = 0; //0 state->nxdn_rcn = 0; state->nxdn_base_freq = 0; state->nxdn_step = 0; state->nxdn_bw = 0; } //Debug/Troubleshooting Option if (c == 90) //'Z' key - Simulate NoCarrier/No VC/CC sync to zero out more stuff (capital Z) { // opts->p25_is_tuned = 0; state->last_cc_sync_time = 0; state->last_vc_sync_time = 0; noCarrier(opts, state); } if (c == 93) //']' key - increment event history indexer { state->eh_index++; } if (c == 91) //'[' key - decrement event history indexer { state->eh_index--; } if (c == 92) //'\' key - toggle events for slot displayed, and reset eh_index { eh_slot ^= 1; state->eh_index = 0; } //attempt retry to TCP Audio server if (c == 56) // '8' key, try audio in type 8 (TCP Audio Server connection) using defaults OR whatever the user last specified { opts->tcp_sockfd = Connect(opts->tcp_hostname, opts->tcp_portno); if (opts->tcp_sockfd != 0) { //reset audio input stream opts->audio_in_file_info = calloc(1, sizeof(SF_INFO)); opts->audio_in_file_info->samplerate=opts->wav_sample_rate; opts->audio_in_file_info->channels=1; opts->audio_in_file_info->seekable=0; opts->audio_in_file_info->format=SF_FORMAT_RAW|SF_FORMAT_PCM_16|SF_ENDIAN_LITTLE; opts->tcp_file_in = sf_open_fd(opts->tcp_sockfd, SFM_READ, opts->audio_in_file_info, 0); if(opts->tcp_file_in == NULL) { fprintf(stderr, "Error, couldn't Connect to TCP with libsndfile: %s\n", sf_strerror(NULL)); } else { //close pulse input if it is currently open if (opts->audio_in_type == 0) closePulseInput(opts); fprintf (stderr, "TCP Socket Connected Successfully.\n"); opts->audio_in_type = 8; } } else fprintf (stderr, "TCP Socket Connection Error.\n"); } if (c == 57) //'9' key, try rigctl connection with default values { //use same or last specified host for TCP audio sink for connection memcpy (opts->rigctlhostname, opts->tcp_hostname, sizeof (opts->rigctlhostname) ); opts->rigctl_sockfd = Connect(opts->rigctlhostname, opts->rigctlportno); if (opts->rigctl_sockfd != 0) opts->use_rigctl = 1; else opts->use_rigctl = 0; } //if trunking and user wants to just go back to the control channel and skip this call if (opts->p25_trunk == 1 && state->p25_cc_freq != 0 && c == 67) //Capital C key - Return to CC { //extra safeguards due to sync issues with NXDN memset (state->nxdn_sacch_frame_segment, 1, sizeof(state->nxdn_sacch_frame_segment)); memset (state->nxdn_sacch_frame_segcrc, 1, sizeof(state->nxdn_sacch_frame_segcrc)); memset (state->active_channel, 0, sizeof(state->active_channel)); //reset dmr blocks dmr_reset_blocks (opts, state); //zero out additional items state->lasttg = 0; state->lasttgR = 0; state->lastsrc = 0; state->lastsrcR = 0; state->payload_algid = 0; state->payload_algidR = 0; state->payload_keyid = 0; state->payload_keyidR = 0; state->payload_mi = 0; state->payload_miR = 0; state->payload_miP = 0; state->payload_miN = 0; opts->p25_is_tuned = 0; state->p25_vc_freq[0] = state->p25_vc_freq[1] = 0; //tune back to the control channel -- NOTE: Doesn't work correctly on EDACS Analog Voice //RIGCTL if (opts->p25_trunk == 1 && opts->use_rigctl == 1) { if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw); SetFreq(opts->rigctl_sockfd, state->p25_cc_freq); } //rtl #ifdef USE_RTLSDR if (opts->p25_trunk == 1 && opts->audio_in_type == 3) rtl_dev_tune (opts, state->p25_cc_freq); #endif state->last_cc_sync_time = time(NULL); //if P25p2 VCH and going back to P25p1 CC, flip symbolrate if (state->p25_cc_is_tdma == 0) { state->samplesPerSymbol = 10; state->symbolCenter = 4; } //if P25p1 Data Revert on P25p2 TDMA CC, flip symbolrate if (state->p25_cc_is_tdma == 1) { state->samplesPerSymbol = 8; state->symbolCenter = 3; } fprintf (stderr, "\n User Activated Return to CC; \n "); } //if trunking or scanning, manually cycle forward through channels loaded (can be run without trunking or scanning enabled) if ( (opts->use_rigctl == 1 || opts->audio_in_type == 3) && c == 76) //Capital L key - Cycle Channels Forward { //extra safeguards due to sync issues with NXDN memset (state->nxdn_sacch_frame_segment, 1, sizeof(state->nxdn_sacch_frame_segment)); memset (state->nxdn_sacch_frame_segcrc, 1, sizeof(state->nxdn_sacch_frame_segcrc)); memset (state->active_channel, 0, sizeof(state->active_channel)); //reset dmr blocks dmr_reset_blocks (opts, state); //zero out additional items state->lasttg = 0; state->lasttgR = 0; state->lastsrc = 0; state->lastsrcR = 0; state->payload_algid = 0; state->payload_algidR = 0; state->payload_keyid = 0; state->payload_keyidR = 0; state->payload_mi = 0; state->payload_miR = 0; state->payload_miP = 0; state->payload_miN = 0; opts->p25_is_tuned = 0; state->p25_vc_freq[0] = state->p25_vc_freq[1] = 0; //just copy and pasted the cycle logic for CC/signal hunting on no sync if (state->lcn_freq_roll >= state->lcn_freq_count) //fixed this to skip the extra wait out at the end of the list { state->lcn_freq_roll = 0; //reset to zero } //roll an extra value up if the current is the same as what's already loaded -- faster hunting on Cap+, etc if (state->lcn_freq_roll != 0) { if (state->trunk_lcn_freq[state->lcn_freq_roll-1] == state->trunk_lcn_freq[state->lcn_freq_roll]) { state->lcn_freq_roll++; //check roll again if greater than expected, then go back to zero if (state->lcn_freq_roll >= state->lcn_freq_count) { state->lcn_freq_roll = 0; //reset to zero } } } //check that we have a non zero value first, then tune next frequency if (state->trunk_lcn_freq[state->lcn_freq_roll] != 0) { //rigctl if (opts->use_rigctl == 1) { if (opts->setmod_bw != 0 ) SetModulation(opts->rigctl_sockfd, opts->setmod_bw); SetFreq(opts->rigctl_sockfd, state->trunk_lcn_freq[state->lcn_freq_roll]); } //rtl if (opts->audio_in_type == 3) { #ifdef USE_RTLSDR rtl_dev_tune (opts, state->trunk_lcn_freq[state->lcn_freq_roll]); #endif } fprintf (stderr, "\n User Activated Channel Cycle;"); fprintf (stderr, " Tuning to Frequency: %.06lf MHz\n", (double)state->trunk_lcn_freq[state->lcn_freq_roll]/1000000); } state->lcn_freq_roll++; state->last_cc_sync_time = time(NULL); //may need to test to see if we want to do the conditional below or not for symbol rate flipping //if P25p2 VCH and going back to P25p1 CC, flip symbolrate if (state->p25_cc_is_tdma == 0) { state->samplesPerSymbol = 10; state->symbolCenter = 4; } //if P25p1 Data Revert on P25p2 TDMA CC, flip symbolrate if (state->p25_cc_is_tdma == 1) { state->samplesPerSymbol = 8; state->symbolCenter = 3; } } if (c == 118 && opts->audio_in_type == 3) //'v' key, cycle rtl volume multiplier, when active { if (opts->rtl_volume_multiplier == 1 || opts->rtl_volume_multiplier == 2) opts->rtl_volume_multiplier++; else opts->rtl_volume_multiplier = 1; } if (c == 86) // 'V' Key, toggle LPF { if (opts->use_lpf == 0) opts->use_lpf = 1; else opts->use_lpf = 0; } if (c == 66) // 'B' Key, toggle HPF { if (opts->use_hpf == 0) opts->use_hpf = 1; else opts->use_hpf = 0; } if (c == 78) // 'N' Key, toggle PBF { if (opts->use_pbf == 0) opts->use_pbf = 1; else opts->use_pbf = 0; } if (c == 72) // 'H' Key, toggle HPF on digital { if (opts->use_hpf_d == 0) opts->use_hpf_d = 1; else opts->use_hpf_d = 0; } if (opts->m17encoder == 1 && c == 92) //'\' key - toggle M17 encoder Encode + TX { if (state->m17encoder_tx == 0) state->m17encoder_tx = 1; else state->m17encoder_tx = 0; //flag on the EOT marker to send last frame after toggling encoder to zero if (state->m17encoder_tx == 0) state->m17encoder_eot = 1; } if(opts->frame_provoice == 1 && c == 65) //'A' Key, toggle ESK mask 0xA0 { if (state->esk_mask == 0) state->esk_mask = 0xA0; else state->esk_mask = 0; } if(opts->frame_provoice == 1 && c == 83) //'S' Key, toggle STD or EA mode and reset { if (state->ea_mode == -1) state->ea_mode = 0; else if (state->ea_mode == 0) state->ea_mode = 1; else state->ea_mode = 0; //reset -- test to make sure these don't do weird things when reset state->edacs_site_id = 0; state->edacs_lcn_count = 0; state->edacs_cc_lcn = 0; state->edacs_vc_lcn = 0; state->edacs_tuned_lcn = -1; state->edacs_vc_call_type = 0; state->p25_cc_freq = 0; opts->p25_is_tuned = 0; state->lasttg = 0; state->lastsrc = 0; } //RTL PPM Manual Adjustment if (c == 125) opts->rtlsdr_ppm_error++; if (c == 123) opts->rtlsdr_ppm_error--; //anything with an entry box will need the inputs and outputs stopped first //so probably just write a function to handle c input, and when c = certain values //needing an entry box, then stop all of those //allocated memory pointer needs to be free'd if (timestr != NULL) { free (timestr); timestr = NULL; } if (datestr != NULL) { free (datestr); datestr = NULL; } } //end ncursesPrinter void ncursesClose () { endwin(); } //init each event history struct passed into here void init_event_history (Event_History_I * event_struct, uint8_t start, uint8_t stop) { for (uint8_t i = start; i < stop; i++) { event_struct->Event_History_Items[i].write = 0; event_struct->Event_History_Items[i].systype = -1; event_struct->Event_History_Items[i].subtype = -1; event_struct->Event_History_Items[i].sys_id1 = 0; event_struct->Event_History_Items[i].sys_id2 = 0; event_struct->Event_History_Items[i].sys_id3 = 0; event_struct->Event_History_Items[i].sys_id4 = 0; event_struct->Event_History_Items[i].sys_id5 = 0; event_struct->Event_History_Items[i].gi = 0; event_struct->Event_History_Items[i].enc = 0; event_struct->Event_History_Items[i].enc_alg = 0; event_struct->Event_History_Items[i].enc_key = 0; event_struct->Event_History_Items[i].mi = 0; event_struct->Event_History_Items[i].svc = 0; event_struct->Event_History_Items[i].source_id = 0; event_struct->Event_History_Items[i].target_id = 0; sprintf (event_struct->Event_History_Items[i].src_str, "%s", "BUMBLEBEETUNA"); sprintf (event_struct->Event_History_Items[i].tgt_str, "%s", "BUMBLEBEETUNA"); sprintf (event_struct->Event_History_Items[i].t_name, "%s", "BUMBLEBEETUNA"); sprintf (event_struct->Event_History_Items[i].s_name, "%s", "BUMBLEBEETUNA"); sprintf (event_struct->Event_History_Items[i].t_mode, "%s", "BUMBLEBEETUNA"); sprintf (event_struct->Event_History_Items[i].s_mode, "%s", "BUMBLEBEETUNA"); event_struct->Event_History_Items[i].channel = 0; event_struct->Event_History_Items[i].event_time = 0; memset (event_struct->Event_History_Items[i].pdu, 0, sizeof(event_struct->Event_History_Items[0].pdu)); sprintf (event_struct->Event_History_Items[i].sysid_string, "%s", ""); sprintf (event_struct->Event_History_Items[i].alias, "%s", "BUMBLEBEETUNA"); sprintf (event_struct->Event_History_Items[i].gps_s, "%s", "BUMBLEBEETUNA"); sprintf (event_struct->Event_History_Items[i].text_message, "%s", "BUMBLEBEETUNA"); sprintf (event_struct->Event_History_Items[i].event_string, "%s", "BUMBLEBEETUNA"); sprintf (event_struct->Event_History_Items[i].internal_str, "%s", "BUMBLEBEETUNA"); } } void push_event_history (Event_History_I * event_struct) { //Fixed, had it going in the wrong direction first time for (uint8_t i = 254; i >= 1; i--) { event_struct->Event_History_Items[i].write = event_struct->Event_History_Items[i-1].write; event_struct->Event_History_Items[i].systype = event_struct->Event_History_Items[i-1].systype; event_struct->Event_History_Items[i].subtype = event_struct->Event_History_Items[i-1].subtype; event_struct->Event_History_Items[i].sys_id1 = event_struct->Event_History_Items[i-1].sys_id1; event_struct->Event_History_Items[i].sys_id2 = event_struct->Event_History_Items[i-1].sys_id2; event_struct->Event_History_Items[i].sys_id3 = event_struct->Event_History_Items[i-1].sys_id3; event_struct->Event_History_Items[i].sys_id4 = event_struct->Event_History_Items[i-1].sys_id4; event_struct->Event_History_Items[i].sys_id5 = event_struct->Event_History_Items[i-1].sys_id5; event_struct->Event_History_Items[i].gi = event_struct->Event_History_Items[i-1].gi; event_struct->Event_History_Items[i].enc = event_struct->Event_History_Items[i-1].enc; event_struct->Event_History_Items[i].enc_alg = event_struct->Event_History_Items[i-1].enc_alg; event_struct->Event_History_Items[i].enc_key = event_struct->Event_History_Items[i-1].enc_key; event_struct->Event_History_Items[i].mi = event_struct->Event_History_Items[i-1].mi; event_struct->Event_History_Items[i].svc = event_struct->Event_History_Items[i-1].svc; event_struct->Event_History_Items[i].source_id = event_struct->Event_History_Items[i-1].source_id; event_struct->Event_History_Items[i].target_id = event_struct->Event_History_Items[i-1].target_id; sprintf (event_struct->Event_History_Items[i].src_str, "%s", event_struct->Event_History_Items[i-1].src_str); sprintf (event_struct->Event_History_Items[i].tgt_str, "%s", event_struct->Event_History_Items[i-1].tgt_str); sprintf (event_struct->Event_History_Items[i].t_name, "%s", event_struct->Event_History_Items[i-1].t_name); sprintf (event_struct->Event_History_Items[i].s_name, "%s", event_struct->Event_History_Items[i-1].s_name); sprintf (event_struct->Event_History_Items[i].t_mode, "%s", event_struct->Event_History_Items[i-1].t_mode); sprintf (event_struct->Event_History_Items[i].s_mode, "%s", event_struct->Event_History_Items[i-1].s_mode); event_struct->Event_History_Items[i].channel = event_struct->Event_History_Items[i-1].channel; event_struct->Event_History_Items[i].event_time = event_struct->Event_History_Items[i+1].event_time; memcpy (event_struct->Event_History_Items[i].pdu, event_struct->Event_History_Items[i-1].pdu, sizeof(event_struct->Event_History_Items[0].pdu)); sprintf (event_struct->Event_History_Items[i].sysid_string, "%s", event_struct->Event_History_Items[i-1].sysid_string); sprintf (event_struct->Event_History_Items[i].alias, "%s", event_struct->Event_History_Items[i-1].alias); sprintf (event_struct->Event_History_Items[i].gps_s, "%s", event_struct->Event_History_Items[i-1].gps_s); sprintf (event_struct->Event_History_Items[i].text_message, "%s", event_struct->Event_History_Items[i-1].text_message); sprintf (event_struct->Event_History_Items[i].event_string, "%s", event_struct->Event_History_Items[i-1].event_string); sprintf (event_struct->Event_History_Items[i].internal_str, "%s", event_struct->Event_History_Items[i-1].internal_str); } } void write_event_to_log_file (dsd_opts * opts, dsd_state * state, uint8_t slot, uint8_t swrite, char * event_string) //pass completed event string here that is in the struct { //open log file FILE * event_log_file; event_log_file = fopen(opts->event_out_file, "a"); fprintf (event_log_file, "%s ", event_string); if (swrite == 1) fprintf (event_log_file, "Slot %d; ", slot+1); fprintf (event_log_file,"\n"); char text_string[2000]; sprintf (text_string, "%s", "BUMBLEBEETUNA"); if (strncmp(text_string, state->event_history_s[slot].Event_History_Items[0].text_message, 13) != 0) fprintf (event_log_file, "%s \n", state->event_history_s[slot].Event_History_Items[0].text_message); if (strncmp(text_string, state->event_history_s[slot].Event_History_Items[0].alias, 13) != 0) fprintf (event_log_file, " Talker Alias: %s \n", state->event_history_s[slot].Event_History_Items[0].alias); if (strncmp(text_string, state->event_history_s[slot].Event_History_Items[0].gps_s, 13) != 0) fprintf (event_log_file, " GPS: %s \n", state->event_history_s[slot].Event_History_Items[0].gps_s); if (strncmp(text_string, state->event_history_s[slot].Event_History_Items[0].internal_str, 13) != 0) fprintf (event_log_file, " DSD-FME: %s \n", state->event_history_s[slot].Event_History_Items[0].internal_str); //flush and close log file fflush (event_log_file); fclose (event_log_file); } // run once per loop to check for and push and update event history void watchdog_event_history (dsd_opts * opts, dsd_state * state, uint8_t slot) { //create a pointer to the current slot event history Event_History_I * event_struct = &state->event_history_s[slot]; //is this a TDMA slot (append Slot value to end of written event history) uint8_t swrite = 0; //who is currently talking uint32_t source_id = 0; //last values pulled from the event history uint32_t last_source_id = event_struct->Event_History_Items[0].source_id; if (slot == 0) source_id = state->lastsrc; else source_id = state->lastsrcR; //if DMR BS or P25P2, then flag the swrite, so write can append slot value to event history log //|| state->lastsynctype == 32 || state->lastsynctype == 33 || state->lastsynctype == 34 MS if (state->lastsynctype == 10 || state->lastsynctype == 11 || state->lastsynctype == 12 || state->lastsynctype == 13) swrite = 1; else if (state->lastsynctype == 35 || state->lastsynctype == 36) swrite = 1; if (slot == 0) //BUGFIX: generic catch on FDMA systems so that we don't write duplicate data to slot 2 event history { //NXDN RID (TODO: Changeover to lastsrc later on) if (state->lastsynctype == 28 || state->lastsynctype == 29) source_id = state->nxdn_last_rid; if (state->lastsynctype == 30 || state->lastsynctype == 30) //YSF Fusion { source_id = 0; for (uint8_t i = 0; i < 11; i++) source_id += state->ysf_src[i]; //convert to sum value to make a distinct enough src value } if (state->lastsynctype == 8 || state->lastsynctype == 9 || state->lastsynctype == 16 || state->lastsynctype == 17) //M17 STR { source_id = (uint32_t)state->m17_src; } if (state->lastsynctype == 6 || state->lastsynctype == 7 || state->lastsynctype == 18 || state->lastsynctype == 19) //DSTAR { source_id = 0; for (uint8_t i = 0; i < 12; i++) source_id += state->dstar_src[i]; //convert to sum value to make a distinct enough src value //need a strncmp here for 8 spaces in this field first so we don't blip a blank into the event history if (strncmp(state->dstar_src, " ", 8) == 0) source_id = 0; } if (state->lastsynctype == 20 || state->lastsynctype == 24 || state->lastsynctype == 21 || state->lastsynctype == 25 || state->lastsynctype == 22 || state->lastsynctype == 26 || state->lastsynctype == 23 || state->lastsynctype == 27) //dPMR { source_id = 0; for (uint8_t i = 0; i < 20; i++) source_id += state->dpmr_caller_id[i]; //convert to sum value to make a distinct enough src value //need a strncmp here for 8 spaces in this field first so we don't blip a blank into the event history if (strncmp(state->dpmr_caller_id, " ", 6) == 0) source_id = 0; } if (state->lastsynctype == 14 || state->lastsynctype == 15 || state->lastsynctype == 37 || state->lastsynctype == 38) //EDACS Calls { source_id = 0; if (opts->p25_is_tuned == 1) source_id = state->lastsrc; } } //call alert beep when new call detected if (last_source_id == 0 && source_id != 0 && opts->call_alert == 1) beeper (opts, state, slot, 40, 86, 3); if (source_id != last_source_id && last_source_id != 0) { if (opts->event_out_file[0] != 0) write_event_to_log_file(opts, state, slot, swrite, event_struct->Event_History_Items[0].event_string); event_struct->Event_History_Items[0].write = 1; //written, or pushed at this point if (slot == 0 && opts->wav_out_f != NULL) { opts->wav_out_f = close_and_rename_wav_file(opts->wav_out_f, opts->wav_out_file, opts->wav_out_dir, event_struct); opts->wav_out_f = open_wav_file(opts->wav_out_dir, opts->wav_out_file, 8000, 0); } else if (slot == 1 && opts->wav_out_fR != NULL) { opts->wav_out_fR = close_and_rename_wav_file(opts->wav_out_fR, opts->wav_out_fileR, opts->wav_out_dir, event_struct); opts->wav_out_fR = open_wav_file(opts->wav_out_dir, opts->wav_out_fileR, 8000, 0); } push_event_history (event_struct); init_event_history (event_struct, 0, 1); //clear out some strings and things memset(state->ysf_txt, 0, sizeof(state->ysf_txt)); memset(state->dstar_gps, 0, sizeof(state->dstar_gps)); memset(state->dstar_txt, 0, sizeof(state->dstar_txt)); state->gi[slot] = -1; //return to an unset value //end of voice call alert if (opts->call_alert == 1) beeper (opts, state, slot, 40, 86, 3); } } //similar to above, but constantly testing and checking the most recent event only //this will hopefully be more useful when dealing with an ongoing event with //features that update over time with embedded signalling, etc void watchdog_event_current (dsd_opts * opts, dsd_state * state, uint8_t slot) { //create a pointer to the current slot event history Event_History_I * event_struct = &state->event_history_s[slot]; //TODO: Flesh out more later on. uint32_t source_id = 0; uint32_t target_id = 0; char src_str[200]; memset (src_str, 0, sizeof(src_str)); sprintf (src_str, "%s", "BUMBLEBEETUNA"); char tgt_str[200]; memset (tgt_str, 0, sizeof(tgt_str)); sprintf (tgt_str, "%s", "BUMBLEBEETUNA"); //group import items char t_name[200]; memset (t_name, 0, sizeof(t_name)); sprintf (t_name, "%s", "BUMBLEBEETUNA"); char s_name[200]; memset (s_name, 0, sizeof(s_name)); sprintf (s_name, "%s", "BUMBLEBEETUNA"); char t_mode[200]; memset (t_mode, 0, sizeof(t_mode)); sprintf (t_mode, "%s", "BUMBLEBEETUNA"); char s_mode[200]; memset (s_mode, 0, sizeof(s_mode)); sprintf (s_mode, "%s", "BUMBLEBEETUNA"); uint16_t svc_opts = 0; uint8_t subtype = 0; uint8_t mfid = 0; uint32_t sys_id1 = 0; uint32_t sys_id2 = 0; uint32_t sys_id3 = 0; uint32_t sys_id4 = 0; uint32_t sys_id5 = 0; uint32_t channel = 0; uint8_t enc = 0; uint8_t alg_id = 0; uint16_t key_id = 0; unsigned long long int mi; char sysid_string[200]; memset(sysid_string, 0, sizeof(sysid_string)); sprintf (sysid_string, "%s", ""); if (slot == 0) { source_id = state->lastsrc; target_id = state->lasttg; subtype = state->dmrburstL; mfid = state->dmr_fid; svc_opts = state->dmr_so; enc = (svc_opts >> 6) & 1; alg_id = state->payload_algid; key_id = (uint16_t)state->payload_keyid; mi = state->payload_mi; } else { source_id = state->lastsrcR; target_id = state->lasttgR; subtype = state->dmrburstR; mfid = state->dmr_fidR; svc_opts = state->dmr_soR; enc = (svc_opts >> 6) & 1; alg_id = state->payload_algidR; key_id = (uint16_t)state->payload_keyidR; mi = state->payload_miR; } //if P25 (if not P25, then these will all be zero anyways) sys_id1 = state->p2_wacn; sys_id2 = state->p2_sysid; if (state->nac != 0) sys_id3 = state->nac; //same as state->p2_cc, but zeroes out when no signal or error else sys_id3 = state->p2_cc; sys_id4 = state->p2_rfssid; sys_id5 = state->p2_siteid; if (sys_id1) sprintf (sysid_string, "P25_%05X%03X%03X_%d_%d", sys_id1, sys_id2, sys_id3, sys_id4, sys_id5); else sprintf (sysid_string, "P25_%03X", sys_id3); if (state->lastsynctype == 10 || state->lastsynctype == 11 || state->lastsynctype == 12 || state->lastsynctype == 13 || state->lastsynctype == 32 || state->lastsynctype == 33 || state->lastsynctype == 34 ) { sys_id1 = state->dmr_t3_syscode; sys_id2 = state->dmr_color_code; if (sys_id1) sprintf (sysid_string, "DMR_%X_CC_%d", sys_id1, sys_id2); else sprintf (sysid_string, "DMR_CC_%d", sys_id2); } if (slot == 0) //BUGFIX: generic catch on FDMA systems so that we don't write duplicate data to slot 2 event history { //NXDN RID (TODO: Changeover to lastsrc and lasttg later on) if (state->lastsynctype == 28 || state->lastsynctype == 29) { source_id = state->nxdn_last_rid; target_id = state->nxdn_last_tg; if (state->nxdn_cipher_type != 0) enc = 1; alg_id = state->nxdn_cipher_type; key_id = state->nxdn_key; sys_id1 = state->nxdn_location_site_code; sys_id2 = state->nxdn_location_sys_code; sys_id3 = state->nxdn_last_ran; //might be an issue on conventional systems that have a different RAN on the tx_rel or idle data bursts if (sys_id1) sprintf (sysid_string, "NXDN_%d_%d_RAN_%d", sys_id2, sys_id1, sys_id3); else sprintf (sysid_string, "NXDN_RAN_%d", sys_id3); } if (state->lastsynctype == 30 || state->lastsynctype == 30) //YSF Fusion { source_id = 0; for (uint8_t i = 0; i < 11; i++) source_id += state->ysf_src[i]; //convert to sum value to make a distinct enough src value //WIP: If Text, compile it here (still having issues with an empty txt string making a line break) uint8_t k = 0; char ysf_emp[21][21]; memset(ysf_emp, 0, sizeof(ysf_emp)); if (memcmp(ysf_emp, state->ysf_txt, sizeof(state->ysf_txt)) != 0) { for (uint8_t i = 4; i < 8; i++) { for (uint8_t j = 0; j < 20; j++) { if (state->ysf_txt[i][j] != 0x2A) event_struct->Event_History_Items[0].text_message[k++] = state->ysf_txt[i][j]; else event_struct->Event_History_Items[0].text_message[k++] = 0x20; //space } event_struct->Event_History_Items[0].text_message[k] = 0; //terminate } } else sprintf (event_struct->Event_History_Items[0].text_message, "%s", "BUMBLEBEETUNA"); sprintf (sysid_string, "%s", "YSF"); char temp_str[20]; memset(temp_str, 0, sizeof(temp_str)); //set src string as a non-spaced non-garbo char string for (uint8_t i = 0; i < 10; i++) { if (state->ysf_src[i] == 0x20) //spaces to underscore temp_str[i] = 0x5F; else if (state->ysf_src[i] > 0x20 && state->ysf_src[i] < 0x7F) //copy normal ascii range characters temp_str[i] = state->ysf_src[i]; else if (state->ysf_src[i] == 0) break; //hit the terminator, so stop else temp_str[i] = 0x5F; //unknown to underscore } sprintf (src_str, "%s", temp_str); //same for tgt str memset(temp_str, 0, sizeof(temp_str)); for (uint8_t i = 0; i < 10; i++) { if (state->ysf_tgt[i] == 0x20) //spaces to underscore temp_str[i] = 0x5F; else if (state->ysf_tgt[i] > 0x20 && state->ysf_tgt[i] < 0x7F) //copy normal ascii range characters temp_str[i] = state->ysf_tgt[i]; else if (state->ysf_tgt[i] == 0) break; //hit the terminator, so stop else temp_str[i] = 0x5F; //unknown to underscore } sprintf (tgt_str, "%s", temp_str); } if (state->lastsynctype == 8 || state->lastsynctype == 9 || state->lastsynctype == 16 || state->lastsynctype == 17) //M17 STR { target_id = (uint32_t)state->m17_dst; source_id = (uint32_t)state->m17_src; sys_id1 = state->m17_can; sprintf (sysid_string, "M17_CAN_%d", sys_id1); sprintf (src_str, "%s", state->m17_src_csd); sprintf (tgt_str, "%s", state->m17_dst_csd); } if (state->lastsynctype == 6 || state->lastsynctype == 7 || state->lastsynctype == 18 || state->lastsynctype == 19) //DSTAR { source_id = 0; for (uint8_t i = 0; i < 12; i++) source_id += state->dstar_src[i]; //convert to sum value to make a distinct enough src value //need a strncmp here for 8 spaces in this field first so we don't blip a blank into the event history if (strncmp(state->dstar_src, " ", 8) == 0) source_id = 0; sprintf (sysid_string, "%s", "DSTAR"); char temp_str[20]; memset(temp_str, 0, sizeof(temp_str)); //set src string as a non-spaced non-garbo char string for (uint8_t i = 0; i < 12; i++) { if (state->dstar_src[i] == 0x20) //spaces to underscore temp_str[i] = 0x5F; else if (state->dstar_src[i] > 0x20 && state->dstar_src[i] < 0x7F) //copy normal ascii range characters temp_str[i] = state->dstar_src[i]; else if (state->dstar_src[i] == 0) break; //hit the terminator, so stop else temp_str[i] = 0x5F; //unknown to underscore } sprintf (src_str, "%s", temp_str); //same for tgt str memset(temp_str, 0, sizeof(temp_str)); for (uint8_t i = 0; i < 8; i++) { if (state->dstar_dst[i] == 0x20) //spaces to underscore temp_str[i] = 0x5F; else if (state->dstar_dst[i] > 0x20 && state->dstar_dst[i] < 0x7F) //copy normal ascii range characters temp_str[i] = state->dstar_dst[i]; else if (state->dstar_dst[i] == 0) break; //hit the terminator, so stop else temp_str[i] = 0x5F; //unknown to underscore } sprintf (tgt_str, "%s", temp_str); } if (state->lastsynctype == 20 || state->lastsynctype == 24 || state->lastsynctype == 21 || state->lastsynctype == 25 || state->lastsynctype == 22 || state->lastsynctype == 26 || state->lastsynctype == 23 || state->lastsynctype == 27) //dPMR { source_id = 0; for (uint8_t i = 0; i < 20; i++) source_id += state->dpmr_caller_id[i]; //convert to sum value to make a distinct enough src value //need a strncmp here for 8 spaces in this field first so we don't blip a blank into the event history if (strncmp(state->dpmr_caller_id, " ", 6) == 0) source_id = 0; sprintf (sysid_string, "DPMR_CC_%d", state->dpmr_color_code); sprintf (src_str, "%s", state->dpmr_caller_id); sprintf (tgt_str, "%s", state->dpmr_target_id); } if (state->lastsynctype == 14 || state->lastsynctype == 15 || state->lastsynctype == 37 || state->lastsynctype == 38) //EDACS Calls { source_id = 0; if (opts->p25_is_tuned == 1) { source_id = state->lastsrc; channel = state->edacs_tuned_lcn; } sys_id1 = state->edacs_site_id; sys_id2 = state->edacs_area_code; sys_id3 = state->edacs_sys_id; svc_opts = state->edacs_vc_call_type; char sup_str[200]; memset (sup_str, 0, sizeof(sup_str)); sprintf (sup_str, "%s", "_"); if (svc_opts & 0x02) strcat (sup_str, "Digital_"); else strcat (sup_str, "Analog_"); if (svc_opts & 0x04) strcat (sup_str, "Emergency_"); if (svc_opts & 0x08) strcat (sup_str, "Group_"); if (svc_opts & 0x10) strcat (sup_str, "I_"); if (svc_opts & 0x20) strcat (sup_str, "ALL_"); if (svc_opts & 0x40) strcat (sup_str, "INTER_"); if (svc_opts & 0x80) strcat (sup_str, "TEST_"); if (svc_opts & 0x100) strcat (sup_str, "AGENCY_"); if (svc_opts & 0x200) strcat (sup_str, "FLEET_"); if (svc_opts & 0x01) strcat (sup_str, "Voice_"); strcat (sup_str, "Call"); sprintf (sysid_string, "EDACS_SITE_%03d", sys_id1); strcat (sysid_string, sup_str); } } //if we have a group_array import, search and load it here //will search and load both target values, and src values if available uint8_t t_name_loaded = 0; uint8_t s_name_loaded = 0; if (target_id != 0) { for (int i = 0; i < state->group_tally; i++) { if (state->group_array[i].groupNumber == target_id) { sprintf (t_name, "%s", state->group_array[i].groupName); sprintf (t_mode, "%s", state->group_array[i].groupMode); t_name_loaded = 1; break; } } } if (source_id != 0) //&& state->gi[slot] == 1 { for (int i = 0; i < state->group_tally; i++) { if (state->group_array[i].groupNumber == source_id) { sprintf (s_name, "%s", state->group_array[i].groupName); sprintf (s_mode, "%s", state->group_array[i].groupMode); s_name_loaded = 1; break; } } } //system type string (P25, DMR, etc) char * sys_string = "Digital"; if (state->lastsynctype != -1) sys_string = SyncTypes[state->lastsynctype]; //date and time strings char * timestr = getTimeN(time(NULL)); char * datestr = getDateN(time(NULL)); if (source_id != 0) { event_struct->Event_History_Items[0].write = 0; if (state->lastsynctype != -1) event_struct->Event_History_Items[0].systype = state->lastsynctype; else event_struct->Event_History_Items[0].systype = 39; //generic digital call event_struct->Event_History_Items[0].subtype = subtype; //voice event_struct->Event_History_Items[0].gi = state->gi[slot]; //need this add this to link control messages event_struct->Event_History_Items[0].sys_id1 = sys_id1; event_struct->Event_History_Items[0].sys_id2 = sys_id2; event_struct->Event_History_Items[0].sys_id3 = sys_id3; event_struct->Event_History_Items[0].sys_id4 = sys_id4; event_struct->Event_History_Items[0].sys_id5 = sys_id5; event_struct->Event_History_Items[0].enc = enc; event_struct->Event_History_Items[0].enc_alg = alg_id; event_struct->Event_History_Items[0].enc_key = key_id; event_struct->Event_History_Items[0].mi = mi; event_struct->Event_History_Items[0].svc = svc_opts; event_struct->Event_History_Items[0].source_id = source_id; event_struct->Event_History_Items[0].target_id = target_id; event_struct->Event_History_Items[0].channel = channel; //need to add this to trunking messages, if tuned from call grant event_struct->Event_History_Items[0].event_time = time(NULL); sprintf (event_struct->Event_History_Items[0].sysid_string, "%s", sysid_string); sprintf (event_struct->Event_History_Items[0].src_str, "%s", src_str); sprintf (event_struct->Event_History_Items[0].tgt_str, "%s", tgt_str); sprintf (event_struct->Event_History_Items[0].t_name, "%s", t_name); sprintf (event_struct->Event_History_Items[0].s_name, "%s", s_name); sprintf (event_struct->Event_History_Items[0].t_mode, "%s", t_mode); sprintf (event_struct->Event_History_Items[0].s_mode, "%s", s_mode); } //Craft an event string for ncurses event history, and a more complex string for logging char event_string[200]; memset(event_string, 0, sizeof(event_string)); //WIP: Seperate Voice Call Event Strings when SRC/TGT values are numerical, //and a seperate one for when they are string values (M17, YSF, DSTAR, and dPMR, or use special formatting) if (state->lastsynctype == 30 || state->lastsynctype == 30) //YSF Fusion //TODO: Data calls dumping a lot of events as VOICE { //TODO: See if we can add some decoded data as well in the future to an event string sprintf (event_string, "%s %s %s TGT: %s SRC: %s ", datestr, timestr, sys_string, state->ysf_tgt, state->ysf_src); } else if (state->lastsynctype == 16 || state->lastsynctype == 17) //M17 { //TODO: See if we can add some decoded data as well in the future to an event string if (state->m17_dst == 0xFFFFFFFFFFFF) sprintf (event_string, "%s %s %s TGT: %s SRC: %s CAN: %02d;", datestr, timestr, sys_string, "BROADCAST", state->m17_src_str, state->m17_can); else sprintf (event_string, "%s %s %s TGT: %s SRC: %s CAN: %02d;", datestr, timestr, sys_string, state->m17_dst_str, state->m17_src_str, state->m17_can); } else if (state->lastsynctype == 6 || state->lastsynctype == 7 || state->lastsynctype == 18 || state->lastsynctype == 19) //DSTAR { //TODO: See if we can add some decoded data as well in the future to an event string sprintf (event_string, "%s %s %s TGT: %s SRC: %s ", datestr, timestr, sys_string, state->dstar_dst, state->dstar_src); } else if (state->lastsynctype == 20 || state->lastsynctype == 24 || state->lastsynctype == 21 || state->lastsynctype == 25 || state->lastsynctype == 22 || state->lastsynctype == 26 || state->lastsynctype == 23 || state->lastsynctype == 27) //dPMR { //TODO: See if we can add some decoded data as well in the future to an event string sprintf (event_string, "%s %s %s CC: %02d; TGT: %s; SRC: %s; ", datestr, timestr, sys_string, state->dpmr_color_code, state->dpmr_target_id, state->dpmr_caller_id); if (state->dPMRVoiceFS2Frame.Version[0] == 3) strcat (event_string, "Scrambler Enc; "); } //TODO: Find out why EDACS is also placing items into Slot 2 Event History with valid src, but invalid tg (not a problem, just odd) else if (state->lastsynctype == 14 || state->lastsynctype == 15 || state->lastsynctype == 37 || state->lastsynctype == 38) //EDACS Calls { //is this AFS format, or EA format, also, need to re-add Ilya's other decoded call elements into this somehow if (state->ea_mode == 1) { sprintf (event_string, "%s %s %s TGT: %07d; SRC: %07d; LCN: %02d; SITE: %d:%d.%04X; ", datestr, timestr, sys_string, target_id, source_id, channel, sys_id1, sys_id2, sys_id3); } else { //TODO: Have EDACS decoder write a nicer string for us to use in this instance // Compute AFS for display purposes only int a = (state->tg_hold >> state->edacs_a_shift) & state->edacs_a_mask; int f = (state->tg_hold >> state->edacs_f_shift) & state->edacs_f_mask; int s = state->tg_hold & state->edacs_s_mask; char afs_str[8]; getAfsString(state, afs_str, a, f, s); sprintf (event_string, "%s %s %s Voice AFS: %s ", datestr, timestr, sys_string, afs_str); } } else if (state->lastsynctype == 10 || state->lastsynctype == 11 || state->lastsynctype == 12 || state->lastsynctype == 13 || state->lastsynctype == 32 || state->lastsynctype == 33 || state->lastsynctype == 34 ) //DMR { if (sys_id1) sprintf (event_string, "%s %s %s TGT: %08d; SRC: %08d; CC: %02d; SYS: %X; ", datestr, timestr, sys_string, target_id, source_id, sys_id2, sys_id1); else sprintf (event_string, "%s %s %s TGT: %08d; SRC: %08d; CC: %02d; ", datestr, timestr, sys_string, target_id, source_id, sys_id2); if (enc) strcat(event_string, "ENC; "); if (alg_id != 0) { char ess_str[30]; sprintf (ess_str, "ALG: %02X; KID: %02X; ", alg_id, key_id); strcat(event_string, ess_str); } //monitor for misc link control that may set a SO without having SO inside of it, //those could cause misc issues here, will need to observe and make adjustments if (svc_opts & 0x80) strcat (event_string, "Emergency; "); if (svc_opts & 0x08) strcat (event_string, "Broadcast; "); if (svc_opts & 0x04) strcat (event_string, "OVCM; "); if (state->gi[slot] == 0) strcat (event_string, "Group; "); else if (state->gi[slot] == 1) strcat (event_string, "Private; "); if (mfid == 0x10) { if (svc_opts & 0x20) strcat (event_string, "TXI; "); else if (svc_opts & 0x10) strcat (event_string, "TXI; "); //this is the svc opt bit that tells you when the next VC6 will be pre-empted, but not helpful here if (svc_opts & 0x03) strcat (event_string, "PRIORITY; "); //need to break this apart into each one, but need to double check the decoded value is accurate } } else if (state->lastsynctype == 0 || state->lastsynctype == 1 || state->lastsynctype == 35 || state->lastsynctype == 36) { if (sys_id1) sprintf (event_string, "%s %s %s TGT: %08d; SRC: %08d; NAC: %03X; NET_STS: %05X:%03X:%d.%d; ", datestr, timestr, sys_string, target_id, source_id, sys_id3, sys_id1, sys_id2, sys_id4, sys_id5); else sprintf (event_string, "%s %s %s TGT: %08d; SRC: %08d; NAC: %03X; ", datestr, timestr, sys_string, target_id, source_id, sys_id3); if (alg_id != 0 && alg_id != 0x80) { char ess_str[30]; sprintf (ess_str, "ENC; ALG: %02X; KID: %04X; ", alg_id, key_id); strcat(event_string, ess_str); } if (svc_opts & 0x80) strcat (event_string, "Emergency; "); if (state->gi[slot] == 0) strcat (event_string, "Group; "); else if (state->gi[slot] == 1) strcat (event_string, "Private; "); } else if (state->lastsynctype == 28 || state->lastsynctype == 29) { if (sys_id1) sprintf (event_string, "%s %s %s TGT: %08d; SRC: %08d; RAN: %02d; SYS: %d.%d; ", datestr, timestr, sys_string, target_id, source_id, sys_id3, sys_id2, sys_id1); else sprintf (event_string, "%s %s %s TGT: %08d; SRC: %08d; RAN: %02d; ", datestr, timestr, sys_string, target_id, source_id, sys_id3); if (enc) strcat(event_string, "ENC; "); if (alg_id != 0) { char ess_str[30]; sprintf (ess_str, "ALG: %d; KID: %02X; ", alg_id, key_id); strcat(event_string, ess_str); } if (state->gi[slot] == 0) strcat (event_string, "Group; "); else if (state->gi[slot] == 1) strcat (event_string, "Private; "); } if (t_name_loaded) { char group[420]; sprintf (group, "TName: %s; Mode: %s; ", t_name, t_mode); strcat (event_string, group); } if (s_name_loaded) { char private[420]; sprintf (private, "SName: %s; Mode: %s; ", s_name, s_mode); strcat (event_string, private); } sprintf (event_struct->Event_History_Items[0].event_string, "%s", event_string); if (timestr != NULL) { free (timestr); timestr = NULL; } if (datestr != NULL) { free (datestr); datestr = NULL; } } void watchdog_event_datacall (dsd_opts * opts, dsd_state * state, uint32_t src, uint32_t dst, char * data_string, uint8_t slot) { UNUSED(opts); state->event_history_s[slot].Event_History_Items[0].write = 0; state->event_history_s[slot].Event_History_Items[0].systype = state->lastsynctype; state->event_history_s[slot].Event_History_Items[0].subtype = 6; //data state->event_history_s[slot].Event_History_Items[0].gi = state->gi[slot]; state->event_history_s[slot].Event_History_Items[0].enc = 0; state->event_history_s[slot].Event_History_Items[0].enc_alg = 0; state->event_history_s[slot].Event_History_Items[0].enc_key = 0; state->event_history_s[slot].Event_History_Items[0].mi = 0; state->event_history_s[slot].Event_History_Items[0].svc = 0; state->event_history_s[slot].Event_History_Items[0].source_id = src; state->event_history_s[slot].Event_History_Items[0].target_id = dst; state->event_history_s[slot].Event_History_Items[0].channel = 0; state->event_history_s[slot].Event_History_Items[0].event_time = time(NULL); //date and time strings //getTimeN(time(NULL)); //getDateN(time(NULL)); char * timestr = getTimeN(time(NULL)); char * datestr = getDateN(time(NULL)); char event_string[2000]; memset (event_string, 0, sizeof(event_string)); sprintf (event_string, "%s %s ", datestr, timestr); strcat (event_string, data_string); //may need to check for potential overflow of this sprintf (state->event_history_s[slot].Event_History_Items[0].event_string, "%s", event_string); //could change this to a strncpy to prevent potential overflow if (timestr != NULL) { free (timestr); timestr = NULL; } if (datestr != NULL) { free (datestr); datestr = NULL; } //call alert on data calls if (opts->call_alert) beeper (opts, state, slot, 80, 20, 3); }