From fae33ca0a10abcc7a03f4057a6e282450314a6ce Mon Sep 17 00:00:00 2001 From: lwvmobile Date: Fri, 9 May 2025 18:36:22 -0400 Subject: [PATCH] Event History Setup: #287; Seperate dsd_ncurses.c into dsd_ncurses_printer.c , dsd_ncurses_menu.c , and dsd_ncurses_handler.c files; Make adjustments as needed to accomodate above; Work on Handling Special Escape Characters with Ncurses Input Handling to prevent rogue things from happening with scrool wheel, etc; --- include/dsd.h | 3 + src/dsd_main.c | 1 + src/dsd_ncurses.c | 4155 ------------------------------------- src/dsd_ncurses_handler.c | 917 ++++++++ src/dsd_ncurses_menu.c | 1629 +++++++++++++++ src/dsd_ncurses_printer.c | 1637 +++++++++++++++ 6 files changed, 4187 insertions(+), 4155 deletions(-) delete mode 100644 src/dsd_ncurses.c create mode 100644 src/dsd_ncurses_handler.c create mode 100644 src/dsd_ncurses_menu.c create mode 100644 src/dsd_ncurses_printer.c diff --git a/include/dsd.h b/include/dsd.h index b6773e9..666d84a 100644 --- a/include/dsd.h +++ b/include/dsd.h @@ -590,6 +590,7 @@ typedef struct int lastsrcR; int8_t gi[2]; //group, or private call, per slot uint8_t eh_index; + uint8_t eh_slot; int nac; int errs; int errs2; @@ -1241,6 +1242,8 @@ void unpack_ambe (uint8_t * input, char * ambe); void ncursesOpen (dsd_opts * opts, dsd_state * state); void ncursesPrinter (dsd_opts * opts, dsd_state * state); +void ncursesMenu (dsd_opts * opts, dsd_state * state); +uint8_t ncurses_input_handler(dsd_opts * opts, dsd_state * state, int c); void ncursesClose (); //new NXDN Functions start here! diff --git a/src/dsd_main.c b/src/dsd_main.c index f772cc6..aa16a2e 100644 --- a/src/dsd_main.c +++ b/src/dsd_main.c @@ -949,6 +949,7 @@ state->gi[0] = -1; state->gi[1] = -1; state->eh_index = 0; + state->eh_slot = 0; state->nac = 0; state->errs = 0; state->errs2 = 0; diff --git a/src/dsd_ncurses.c b/src/dsd_ncurses.c deleted file mode 100644 index a966903..0000000 --- a/src/dsd_ncurses.c +++ /dev/null @@ -1,4155 +0,0 @@ -/*------------------------------------------------------------------------------- - * 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" -#include "synctype.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]; -static unsigned long long int edacs_channel_tree[33][6]; - -char * FM_bannerN[9] = { - " ", - " ██████╗ ██████╗██████╗    ███████╗███╗ ███╗███████╗ ", - " ██╔══██╗██╔════╝██╔══██╗   ██╔════╝████╗ ████║██╔════╝ ", - " ██║ ██║╚█████╗ ██║ ██║   █████╗ ██╔████╔██║█████╗ ", - " ██║ ██║ ╚═══██╗██║ ██║   ██╔══╝ ██║╚██╔╝██║██╔══╝ ", - " ██████╔╝██████╔╝██████╔╝   ██║ ██║ ╚═╝ ██║███████╗ ", - " ╚═════╝ ╚═════╝ ╚═════╝    ╚═╝ ╚═╝ ╚═╝╚══════╝ ", - " ", - " " -}; - -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 - -}; - -time_t nowN; - -//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(); - - //initialize this - memset (edacs_channel_tree, 0, sizeof(edacs_channel_tree)); - -} - -//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; - if (state->ea_mode == 0 && state->lastsrc == 0x800) //this was from a grant update, so set this to 0 - edacs_channel_tree[state->edacs_vc_lcn][3] = 0; - 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(); - -} diff --git a/src/dsd_ncurses_handler.c b/src/dsd_ncurses_handler.c new file mode 100644 index 0000000..4e9129a --- /dev/null +++ b/src/dsd_ncurses_handler.c @@ -0,0 +1,917 @@ +/*------------------------------------------------------------------------------- +* dsd_ncurses_handler.c +* DSD-FME ncurses terminal user input handler +* +* LWVMOBILE +* 2025-05 DSD-FME Florida Man Edition +*-----------------------------------------------------------------------------*/ + +#include "dsd.h" + +uint8_t ncurses_input_handler(dsd_opts * opts, dsd_state * state, int c) +{ + + struct stat st_wav = {0}; + + // //debug char value + // if (c != -1) + // fprintf (stderr, "\n User Input: %i / %c ;;", c, c); + + //TEST: Handle Escape Characters + //if issues arise, just go back to the way it was before + //this is primarly a fix for scroll wheel activatign the menu + //and Windows Powershell right-click doing a copy and paste + //and sendign tons of garbage getch chars here and changing things + if (c == '\033') + { + //TODO: Find way to getch all the chars immediately after, or just + //run getch in a loop in case of somebody copying and pasting a ton of things accidentally + for (int i = 0; i < 100000; i++) //wonder how slow this will be + getch(); + c = -1; + return 1; + } + + //keyboard shortcuts - codes same as ascii codes + if (c == 10) //Return / Enter 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 + { + //for filenames (no colons, etc) + char * timestr = getTime(); + char * datestr = getDate(); + sprintf (opts->symbol_out_file, "%s_%s_dibit_capture.bin", datestr, timestr); + openSymbolOutFile (opts, state); + //allocated memory pointer needs to be free'd + if (timestr != NULL) + { + free (timestr); + timestr = NULL; + } + if (datestr != NULL) + { + free (datestr); + datestr = NULL; + } + } + + 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 + { + state->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 + + return 0; + +} //end ncursesPrinter \ No newline at end of file diff --git a/src/dsd_ncurses_menu.c b/src/dsd_ncurses_menu.c new file mode 100644 index 0000000..5feeb30 --- /dev/null +++ b/src/dsd_ncurses_menu.c @@ -0,0 +1,1629 @@ +/*------------------------------------------------------------------------------- +* dsd_ncurses_menu.c +* DSD-FME ncurses terminal menu system +* +* LWVMOBILE +* 2025-05 DSD-FME Florida Man Edition +*-----------------------------------------------------------------------------*/ + +#include "dsd.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; + +//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); +} + +//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(); + + //retune dongle if frequency is not zero + if (opts->rtlsdr_center_freq != 0) + rtl_dev_tune (opts, 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 + { + + #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 diff --git a/src/dsd_ncurses_printer.c b/src/dsd_ncurses_printer.c new file mode 100644 index 0000000..1d5a963 --- /dev/null +++ b/src/dsd_ncurses_printer.c @@ -0,0 +1,1637 @@ +/*------------------------------------------------------------------------------- + * dsd_ncurses_printer.c + * DSD-FME ncurses open, printer, and close + * + * ASCII art generated by: + * https://fsymbols.com/generators/carty/ + * + * LWVMOBILE + * 2025-05 DSD-FME Florida Man Edition + * + * ilyacodes + * 2024-03 EDACS-FME display improvements + *-----------------------------------------------------------------------------*/ + + +#include "dsd.h" +#include "git_ver.h" +#include "synctype.h" + +char mbeversionstr[25]; //MBElib version string +static unsigned long long int edacs_channel_tree[33][6]; + +char * FM_bannerN[9] = { + " ", + " ██████╗ ██████╗██████╗    ███████╗███╗ ███╗███████╗ ", + " ██╔══██╗██╔════╝██╔══██╗   ██╔════╝████╗ ████║██╔════╝ ", + " ██║ ██║╚█████╗ ██║ ██║   █████╗ ██╔████╔██║█████╗ ", + " ██║ ██║ ╚═══██╗██║ ██║   ██╔══╝ ██║╚██╔╝██║██╔══╝ ", + " ██████╔╝██████╔╝██████╔╝   ██║ ██║ ╚═╝ ██║███████╗ ", + " ╚═════╝ ╚═════╝ ╚═════╝    ╚═╝ ╚═╝ ╚═╝╚══════╝ ", + " ", + " " +}; + +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 ncursesOpen (dsd_opts * opts, dsd_state * state) +{ + + UNUSED(opts); UNUSED(state); + + // state->menuopen = 1; //flag the menu is open, stop processing getFrameSync + mbe_printVersion (mbeversionstr); + 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(); + + //initialize this + memset (edacs_channel_tree, 0, sizeof(edacs_channel_tree)); + +} + +void +ncursesPrinter (dsd_opts * opts, dsd_state * state) +{ + uint8_t idas = 0; + int level = 0; + int c = 0; + + int lls = -1; + int i = 0; + + if (opts->audio_in_type != 1) //can't run getch/menu when using STDIN - + { + timeout(0); // + c = getch(); // + } + + //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; + if (state->ea_mode == 0 && state->lastsrc == 0x800) //this was from a grant update, so set this to 0 + edacs_channel_tree[state->edacs_vc_lcn][3] = 0; + 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 (" ENTER 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", mbeversionstr); + #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 + } + 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", state->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 = state->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(); + + //handle input + ncurses_input_handler(opts, state, c); + +} + +void ncursesClose () +{ + endwin(); + +}