Files
dsd-fme/src/dsd_ncurses.c
2023-08-02 22:48:17 -04:00

4019 lines
123 KiB
C
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*-------------------------------------------------------------------------------
* 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
*-----------------------------------------------------------------------------*/
//TODO: Simulate Tone by playing AMBE FF32D2D2D2D880 x 4 on call alert (NXDN Foreign Lady Chirp on End)
#include "dsd.h"
#include "git_ver.h"
#define BSIZE 999
#define UDP_BUFLEN 5 //maximum UDP buffer length
#define SRV_IP "127.0.0.1" //IP
#define UDP_PORT 6020 //UDP port
uint32_t temp_freq = -1;
//
//struct for checking existence of directory to write to
struct stat st_wav = {0};
int reset = 0;
int tg;
int tgR;
int tgn;
int rd;
int rdR;
int rn;
int nc;
int src;
int lls = -1;
int dcc = -1;
int i = 0;
char versionstr[25];
unsigned long long int call_matrix[33][6];
char * FM_bannerN[9] = {
" ",
" ██████╗ ██████╗██████╗    ███████╗███╗ ███╗███████╗ ",
" ██╔══██╗██╔════╝██╔══██╗   ██╔════╝████╗ ████║██╔════╝ ",
" ██║ ██║╚█████╗ ██║ ██║   █████╗ ██╔████╔██║█████╗ ",
" ██║ ██║ ╚═══██╗██║ ██║   ██╔══╝ ██║╚██╔╝██║██╔══╝ ",
" ██████╔╝██████╔╝██████╔╝   ██║ ██║ ╚═╝ ██║███████╗ ",
" ╚═════╝ ╚═════╝ ╚═════╝    ╚═╝ ╚═╝ ╚═╝╚══════╝ ",
" ",
" "
};
char * SyncTypes[44] = {
"P25P1",
"P25P1",
"X2TDMA DATA",
"X2TDMA DATA",
"X2TDMA VOICE",
"X2TDMA VOICE",
"DSTAR", //voice
"DSTAR", //voice
"NXDN VOICE",
"NXDN VOICE",
"DMR", //10
"DMR",
"DMR",
"DMR",
"EDACS/PV",
"EDACS/PV",
"NXDN VOICE", //DATA
"NXDN VOICE", //DATA
"DSTAR", //header
"DSTAR", //header
"dPMR", //20
"dPMR",
"dPMR",
"dPMR",
"dPMR",
"dPMR",
"dPMR",
"dPMR",
"NXDN",
"NXDN",
"YSF", //30
"YSF", //31
"DMR",
"DMR",
"DMR",
"P25P2",
"P25P2",
"EDACS/PV", //37
"EDACS/PV", //38
"",
""
};
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
};
//Sorry Remus :(
void beeper (dsd_opts * opts, dsd_state * state, int lr)
{
UNUSED(state);
int i, j, n;
//use lr as left or right channel designation in stereo config
float samp_f[160]; //mono float sample
float samp_fs[320]; //stereo float sample
short samp_s[160]; //mono short sample
short samp_ss[320]; //stereo short sample
short samp_su[960]; //mono short upsample
short outbuf[6]; //temp storage for upsample
n = 0; //rolling sine wave 'degree'
//each j increment is 20 ms at 160 samples / 8 kHz
for (j = 0; j < 3; j++)
{
//'zero' out stereo mix samples
memset (samp_fs, 0.1f, sizeof(samp_fs));
memset (samp_ss, 0, sizeof(samp_ss));
//generate a tone (ID=45, AD=103, rolling n value)
soft_tonef(samp_f, n, 45, 103);
//convert float to short if required
if (opts->floating_point == 0)
{
mbe_floattoshort(samp_f, samp_s);
for (i = 0; i < 160; i++)
{
samp_s[i] *= 4000; //apply gain
samp_ss[(i*2)+lr] = samp_s[i];
}
}
//load returned tone sample into appropriate channel -- left = +0; right = +1;
for (i = 0; i < 160; i++)
samp_fs[(i*2)+lr] = samp_f[i];
//play sample 3 times (20ms x 3 = 60ms)
if (opts->audio_out_type == 0) //Pulse Audio
{
if (opts->pulse_digi_out_channels == 2 && opts->floating_point == 1)
pa_simple_write(opts->pulse_digi_dev_out, samp_fs, 320*4, NULL);
if (opts->pulse_digi_out_channels == 1 && opts->floating_point == 1)
pa_simple_write(opts->pulse_digi_dev_out, samp_f, 160*4, NULL);
if (opts->pulse_digi_out_channels == 2 && opts->floating_point == 0)
pa_simple_write(opts->pulse_digi_dev_out, samp_ss, 320*2, NULL);
if (opts->pulse_digi_out_channels == 1 && opts->floating_point == 0)
pa_simple_write(opts->pulse_digi_dev_out, samp_s, 160*2, NULL);
}
else if (opts->audio_out_type == 1) //STDOUT
{
if (opts->pulse_digi_out_channels == 2 && opts->floating_point == 1)
write(opts->audio_out_fd, samp_fs, 320*4);
if (opts->pulse_digi_out_channels == 1 && opts->floating_point == 1)
write(opts->audio_out_fd, samp_f, 160*4);
if (opts->pulse_digi_out_channels == 2 && opts->floating_point == 0)
write(opts->audio_out_fd, samp_ss, 320*2);
if (opts->pulse_digi_out_channels == 1 && opts->floating_point == 0)
write(opts->audio_out_fd, samp_s, 160*2);
}
else if (opts->audio_out_type == 2) //OSS Variable Output (no float)
{
if (opts->pulse_digi_out_channels == 2 && opts->floating_point == 0)
write(opts->audio_out_fd, samp_ss, 320*2);
if (opts->pulse_digi_out_channels == 1 && opts->floating_point == 0)
write(opts->audio_out_fd, samp_s, 160*2);
}
else if (opts->audio_out_type == 5) //OSS 48k/1 configuration with upsample
{
short prev = 0;
for (i = 0; i < 160; i++)
{
upsampleS (samp_s[i], prev, outbuf);
samp_su[(i*6)+0] = outbuf[0];
samp_su[(i*6)+1] = outbuf[1];
samp_su[(i*6)+2] = outbuf[2];
samp_su[(i*6)+3] = outbuf[3];
samp_su[(i*6)+4] = outbuf[4];
samp_su[(i*6)+5] = outbuf[5];
}
write (opts->audio_out_fd, samp_su, 960*2);
}
}
}
char * getDateN(void) {
char datename[80]; //bug in 32-bit Ubuntu when using date in filename, date is garbage text
char * curr2;
struct tm * to;
time_t t;
t = time(NULL);
to = localtime( & t);
strftime(datename, sizeof(datename), "%Y-%m-%d", to);
curr2 = strtok(datename, " ");
return curr2;
}
time_t nowN;
char * getTimeN(void) //get pretty hh:mm:ss timestamp
{
time_t t = time(NULL);
char * curr;
char * stamp = asctime(localtime( & t));
curr = strtok(stamp, " ");
curr = strtok(NULL, " ");
curr = strtok(NULL, " ");
curr = strtok(NULL, " ");
return curr;
}
char * getDateC(time_t t) {
char datename[99]; //Ubuntu 32-bit, use 80; everything else, use 99
char * curr2;
struct tm * to;
to = localtime( & t);
strftime(datename, sizeof(datename), "%Y-%m-%d", to);
curr2 = strtok(datename, " ");
return curr2;
}
char * getTimeC(time_t t) //get pretty hh:mm:ss timestamp
{
char * curr;
char * stamp = asctime(localtime( & t));
curr = strtok(stamp, " ");
curr = strtok(NULL, " ");
curr = strtok(NULL, " ");
curr = strtok(NULL, " ");
return curr;
}
//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)",
"Save Signal to Symbol Capture Bin",
"Toggle Muting Encrypted Traffic ",
"Save Per Call Decoded WAV (AUTO/TDMA and NXDN)",
"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 Call 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)
{
UNUSED2(opts, state);
// state->menuopen = 1; //flag the menu is open, stop processing getFrameSync
mbe_printVersion (versionstr);
setlocale(LC_ALL, "");
initscr(); //Initialize NCURSES screen window
start_color();
#ifdef PRETTY_COLORS
init_pair(1, COLOR_YELLOW, COLOR_BLACK); //Yellow/Amber for frame sync/control channel, NV style
init_pair(2, COLOR_RED, COLOR_BLACK); //Red for Terminated Calls
init_pair(3, COLOR_GREEN, COLOR_BLACK); //Green for Active Calls
init_pair(4, COLOR_CYAN, COLOR_BLACK); //Cyan for Site Extra and Patches
init_pair(5, COLOR_MAGENTA, COLOR_BLACK); //Magenta for no frame sync/signal
init_pair(6, COLOR_WHITE, COLOR_BLACK); //White Card Color Scheme
#else
init_pair(1, COLOR_WHITE, COLOR_BLACK); //White Card Color Scheme
init_pair(2, COLOR_WHITE, COLOR_BLACK); //White Card Color Scheme
init_pair(3, COLOR_WHITE, COLOR_BLACK); //White Card Color Scheme
init_pair(4, COLOR_WHITE, COLOR_BLACK); //White Card Color Scheme
init_pair(5, COLOR_WHITE, COLOR_BLACK); //White Card Color Scheme
init_pair(6, COLOR_WHITE, COLOR_BLACK); //White Card Color Scheme
#endif
noecho();
cbreak();
}
//ncursesMenu
void ncursesMenu (dsd_opts * opts, dsd_state * state)
{
//update sync time on cc sync so we don't immediately go CC hunting when exiting the menu
state->last_cc_sync_time = time(NULL);
//close pulse output if not null output
if (opts->audio_out == 1 && opts->audio_out_type == 0)
{
closePulseOutput (opts);
}
//close OSS output
if (opts->audio_out_type == 2 || opts->audio_out_type == 5)
{
close (opts->audio_out_fd);
}
if (opts->audio_in_type == 0) //close pulse input if it is the specified input method
{
closePulseInput(opts);
}
if (opts->audio_in_type == 3)
{
#ifdef USE_RTLSDR
rtl_clean_queue();
#endif
}
if (opts->audio_in_type == 8) //close TCP input SF file so we don't buffer audio while not decoding
{
sf_close(opts->tcp_file_in);
}
if (opts->audio_in_type == 5) //close UDP input SF file so we don't buffer audio while not decoding
{
// sf_close(opts->udp_file_in); //disable for testing
}
state->payload_keyid = 0;
state->payload_keyidR = 0;
//zero out to fix call history 'scrolling' bug when changing decoding types
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)
{
sprintf (opts->wav_out_file, "%s %s DSD-FME-DECODED.wav", getDateN(), getTimeN());
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);
}
opts->dmr_stereo_wav = 1;
//catch all in case of no file name set, won't crash or something
sprintf (opts->wav_out_file, "./%s/DSD-FME-X1.wav", opts->wav_out_dir); //
sprintf (opts->wav_out_fileR, "./%s/DSD-FME-X2.wav", opts->wav_out_dir); //
openWavOutFileL (opts, state);
openWavOutFileR (opts, state);
}
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, " Enter RTL Device Index 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, " Enter 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, " 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();
entry_win = newwin(6, WIDTH+18, starty+10, startx+10);
box (entry_win, 0, 0);
mvwprintw(entry_win, 2, 2, " Enter 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, " Enter 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, " Enter RTL UDP Port - Optional (Default = 0)");
mvwprintw(entry_win, 3, 3, " ");
echo();
refresh();
wscanw(entry_win, "%d", &opts->rtl_udp_port);
noecho();
entry_win = newwin(8, WIDTH+22, starty+10, startx+10);
box (entry_win, 0, 0);
mvwprintw(entry_win, 2, 2, " Enter RTL RMS Squelch Level (NXDN/dPMR only)");
mvwprintw(entry_win, 3, 3, " ");
echo();
refresh();
wscanw(entry_win, "%d", &opts->rtl_squelch_level);
noecho();
entry_win = newwin(17, 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", opts->rtl_dev_index);
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 Device UDP Port: %d", opts->rtl_udp_port);
mvwprintw(entry_win, 9, 2, " RTL Device PPM: %d", opts->rtlsdr_ppm_error);
mvwprintw(entry_win, 10, 2, " RTL RMS Squelch: %d", opts->rtl_squelch_level);
mvwprintw(entry_win, 12, 2, " Are You Sure?");
mvwprintw(entry_win, 13, 2, " 1 = Yes, 2 = No ");
mvwprintw(entry_win, 14, 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
}
#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;
}
state->audio_smoothing = 0; //disable smoothing to prevent random crackling/buzzing
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)
{
//flesh out all closewavs and sprint "" wav filenames
closeWavOutFile (opts, state);
closeWavOutFileL (opts, state);
closeWavOutFileR (opts, state);
//closeWavOutFileRaw (opts, state);
sprintf (opts->wav_out_file, "%s", "");
sprintf (opts->wav_out_fileR, "%s", "");
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();
}
//Privacy Key Entry
if (choice == 14)
{
state->payload_keyid = 0;
state->payload_keyidR = 0;
short int option = 0;
entry_win = newwin(14, 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 - **tera 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 Key ");
mvwprintw(entry_win, 10, 2, "6 - Force DMR RC4 Priority/LE ");
mvwprintw(entry_win, 11, 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 Key Value (HEX):");
mvwprintw(entry_win, 3, 3, " ");
echo();
refresh();
wscanw(entry_win, "%llX", &state->R);
noecho();
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;
}
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;
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 call history (usually if janky output when switching modes)
{
for (short int k = 0; k <= 9; k++)
{
call_matrix[k][0] = 0;
call_matrix[k][1] = 0;
call_matrix[k][2] = 0;
call_matrix[k][3] = 0;
call_matrix[k][4] = 0;
call_matrix[k][5] = 0;
}
src = 0;
rn = 0;
tgn = 0;
dcc = -1;
tg = 0;
tgR = 0;
rd = 0;
rdR = 0;
state->lastsrc = 0;
state->lastsrcR = 0;
state->lasttg = 0;
state->lasttgR = 0;
}
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); //run one rep to clear menu boxes out
#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);
}
if (opts->audio_in_type == 5) //re-open UDP input 'file'
{
// opts->udp_file_in = sf_open_fd(opts->udp_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;
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 && state->synctype < 39)
{
lls = state->synctype;
}
//NXDN -- I really need to fix this better, but this is good enough for today
if (state->nxdn_last_rid != 0 && state->nxdn_last_rid != src)
{
src = state->nxdn_last_rid;
}
if (state->nxdn_last_ran != -1 )
{
rn = state->nxdn_last_ran;
}
if (state->nxdn_last_tg != 0 && state->nxdn_last_tg != tgn)
{
tgn = state->nxdn_last_tg;
}
//DMR CC
if (state->color_code_ok && state->dmr_color_code != -1 && (lls == 12 || lls == 13 || lls == 10 || lls == 11 || lls == 32 || lls == 33) )
{
dcc = state->dmr_color_code;
}
//DMR SRC
if ( (lls == 12 || lls == 13 || lls == 10 || lls == 11 || lls == 32) )
//if ( (lls == 12 || lls == 11 || lls == 32) )
{
//if (state->dmrburstL == 16 && state->lastsrc > 0) //state->currentslot == 0 &&
if (state->lastsrc > 0)
{
rd = state->lastsrc;
}
//if (state->dmrburstR == 16 && state->lastsrcR > 0) //state->currentslot == 1 &&
if (state->lastsrcR > 0)
{
rdR = state->lastsrcR;
}
}
//DMR TG
if ( (lls == 12 || lls == 13 || lls == 10 || lls == 11 || lls == 32) )
//if ( (lls == 12 || lls == 11 || lls == 32) )
{
//if (state->dmrburstL == 16 && state->lasttg > 0) //state->currentslot == 0 &&
if (state->lasttg > 0)
{
tg = state->lasttg;
}
//if (state->dmrburstR == 16 && state->lasttgR > 0) //state->currentslot == 1 &&
if (state->lasttgR > 0)
{
tgR = state->lasttgR;
}
}
//P25 P1 and VCH0
if (state->p2_cc > 0)
{
nc = state->p2_cc;
}
if ( state->lasttg > 0 && (lls == 0 || lls == 1 || lls == 35 || lls == 36) )
{
tg = state->lasttg;
}
if ( state->lastsrc > 0 && (lls == 0 || lls == 1 || lls == 35 || lls == 36) )
{
rd = state->lastsrc;
}
//P25 P2 VCH2
if (state->lasttgR > 0 && (lls == 35 || lls == 36) )
{
tgR = state->lasttgR;
}
if (state->lastsrcR > 0 && (lls == 35 || lls == 36) )
{
rdR = state->lastsrcR;
}
//P25 P2 NAC to dcc for matrix shim
if (state->p2_cc > 0 && (lls == 35 || lls == 36) )
{
dcc = state->p2_cc;
}
//Call History Matrix Shuffling
//Edacs - ProVoice
if ( (lls == 14 || lls == 15 || lls == 37 || lls == 38) && state->carrier == 1)
{
if (state->edacs_vc_lcn != -1)
{
call_matrix[state->edacs_vc_lcn][0] = lls;
call_matrix[state->edacs_vc_lcn][1] = state->edacs_vc_lcn;
call_matrix[state->edacs_vc_lcn][2] = state->lasttg;
call_matrix[state->edacs_vc_lcn][3] = state->lastsrc;
call_matrix[state->edacs_vc_lcn][4] = 1;
call_matrix[state->edacs_vc_lcn][5] = time(NULL);
}
}
//DSTAR
// if ( (lls == 6 || lls == 7 || lls == 18 || lls == 19) && (time(NULL) - call_matrix[9][5] > 5) && state->carrier == 1)
// {
// for (short int k = 0; k < 9; k++)
// {
// call_matrix[k][0] = call_matrix[k+1][0];
// call_matrix[k][1] = call_matrix[k+1][1];
// call_matrix[k][2] = call_matrix[k+1][2];
// call_matrix[k][3] = call_matrix[k+1][3];
// call_matrix[k][4] = call_matrix[k+1][4];
// call_matrix[k][5] = call_matrix[k+1][5];
// }
// call_matrix[9][0] = lls;
// call_matrix[9][1] = 1;
// call_matrix[9][2] = 1;
// call_matrix[9][3] = 1;
// call_matrix[9][4] = 1;
// call_matrix[9][5] = time(NULL);
// }
//NXDN -- I really need to fix this better, but this is good enough for today
if ( call_matrix[9][2] != src && src != 0 && rn != -1 )
{
for (short int k = 0; k < 9; k++)
{
call_matrix[k][0] = call_matrix[k+1][0];
call_matrix[k][1] = call_matrix[k+1][1];
call_matrix[k][2] = call_matrix[k+1][2];
call_matrix[k][3] = call_matrix[k+1][3];
call_matrix[k][4] = call_matrix[k+1][4];
call_matrix[k][5] = call_matrix[k+1][5];
}
call_matrix[9][0] = lls;
call_matrix[9][1] = rn;
call_matrix[9][2] = src;
call_matrix[9][3] = 0;
call_matrix[9][4] = tgn;
call_matrix[9][5] = time(NULL);
//open wav file if enabled and both rd and tg are not 0
if (opts->dmr_stereo_wav == 1 && src != 0 ) //&& tgn != 0, some TG can be 0 on NXDN
{
//close old first, assign name based on time and radio, open wav file
closeWavOutFileL (opts, state);
sprintf (opts->wav_out_file, "./%s/%s %s NXDN - RAN %d - TGT %d - SRC %d.wav", opts->wav_out_dir, getDateN(), getTimeN(), rn, tgn, src);
openWavOutFileL (opts, state); //testing for now, will want to move to per call later
}
if (opts->call_alert == 1)
{
beeper (opts, state, 0);
}
}
//DMR MS
if ( call_matrix[9][2] != rd && lls == 32)
{
for (short int k = 0; k < 10; k++)
{
call_matrix[k][0] = call_matrix[k+1][0];
call_matrix[k][1] = call_matrix[k+1][1];
call_matrix[k][2] = call_matrix[k+1][2];
call_matrix[k][3] = call_matrix[k+1][3];
call_matrix[k][4] = call_matrix[k+1][4];
call_matrix[k][5] = call_matrix[k+1][5];
}
call_matrix[9][0] = lls;
call_matrix[9][1] = tg;
call_matrix[9][2] = rd;
call_matrix[9][3] = 1; //hard set slot number
call_matrix[9][4] = dcc;
call_matrix[9][5] = time(NULL);
//open wav file if enabled and both rd and tg are not 0
if (opts->dmr_stereo_wav == 1 && rd != 0 && tg != 0)
{
//close old first, assign name based on time and radio, open wav file
closeWavOutFileL (opts, state);
sprintf (opts->wav_out_file, "./%s/%s %s MS - CC %d - TG %d - RD %d.wav", opts->wav_out_dir, getDateN(), getTimeN(), dcc, tg, rd);
openWavOutFileL (opts, state); //testing for now, will want to move to per call later
}
if (opts->call_alert == 1 && rd != 0 && tg != 0)
{
//fprintf (stderr, "BEEP 0 MS LEFT\n");
beeper (opts, state, 0);
state->dmr_end_alert[0] = 0; //new voice frame, okay to beep at the end of it
}
memset(state->dmr_alias_block_segment[0], 0, sizeof(state->dmr_alias_block_segment[0]));
sprintf (state->dmr_embedded_gps[0], "%s", "");
}
//DMR BS Slot 1 - matrix 0-4
if ( call_matrix[4][2] != rd && (lls == 11 || lls == 12 || lls == 10 || lls == 13 || lls == 35 || lls == 36) )
{
for (short int k = 0; k < 4; k++)
{
call_matrix[k][0] = call_matrix[k+1][0];
call_matrix[k][1] = call_matrix[k+1][1];
call_matrix[k][2] = call_matrix[k+1][2];
call_matrix[k][3] = call_matrix[k+1][3];
call_matrix[k][4] = call_matrix[k+1][4];
call_matrix[k][5] = call_matrix[k+1][5];
}
call_matrix[4][0] = lls;
call_matrix[4][1] = tg;
call_matrix[4][2] = rd;
call_matrix[4][3] = 1; //hard set slot number
call_matrix[4][4] = dcc;
call_matrix[4][5] = time(NULL);
//open wav file if enabled and both rd and tg are not 0
if (opts->dmr_stereo_wav == 1 && rd != 0 && tg != 0)
{
//close old first, assign name based on time and radio, open wav file
closeWavOutFileL (opts, state);
sprintf (opts->wav_out_file, "./%s/%s %s CC %d - TG %d - RD %d.wav", opts->wav_out_dir, getDateN(), getTimeN(), dcc, tg, rd);
openWavOutFileL (opts, state); //testing for now, will want to move to per call later
}
if (opts->call_alert == 1 && rd != 0 && tg != 0)
{
//fprintf (stderr, "BEEP 0 BS LEFT\n");
beeper (opts, state, 0);
state->dmr_end_alert[0] = 0; //new voice frame, okay to beep at the end of it
}
memset(state->dmr_alias_block_segment[0], 0, sizeof(state->dmr_alias_block_segment[0]));
sprintf (state->dmr_embedded_gps[0], "%s", "");
}
//DMR BS Slot 2 - matrix 5-9
if ( call_matrix[9][2] != rdR && (lls == 11 || lls == 12 || lls == 10 || lls == 13 || lls == 35 || lls == 36) )
{
for (short int k = 5; k < 9; k++)
{
call_matrix[k][0] = call_matrix[k+1][0];
call_matrix[k][1] = call_matrix[k+1][1];
call_matrix[k][2] = call_matrix[k+1][2];
call_matrix[k][3] = call_matrix[k+1][3];
call_matrix[k][4] = call_matrix[k+1][4];
call_matrix[k][5] = call_matrix[k+1][5];
}
call_matrix[9][0] = lls;
call_matrix[9][1] = tgR;
call_matrix[9][2] = rdR;
call_matrix[9][3] = 2; //hard set slot number
call_matrix[9][4] = dcc;
call_matrix[9][5] = time(NULL);
//open wav file if enabled and both rdR and tgR are not 0
if (opts->dmr_stereo_wav == 1 && rdR != 0 && tgR != 0)
{
//close old first, assign name based on time and radio, open wav file
closeWavOutFileR (opts, state);
sprintf (opts->wav_out_fileR, "./%s/%s %s CC %d - TG %d - RD %d.wav", opts->wav_out_dir, getDateN(), getTimeN(), dcc, tgR, rdR);
openWavOutFileR (opts, state); //testing for now, will want to move to per call later
}
if (opts->call_alert == 1 && rdR != 0 && tgR != 0)
{
//fprintf (stderr, "BEEP 1 BS RIGHT\n");
beeper (opts, state, 1);
state->dmr_end_alert[1] = 0; //new voice frame, okay to beep at the end of it
}
memset(state->dmr_alias_block_segment[1], 0, sizeof(state->dmr_alias_block_segment[1]));
sprintf (state->dmr_embedded_gps[1], "%s", "");
}
//P25 P1
if ( (lls == 0 || lls == 1) && call_matrix[9][2] != rd && nc > 0 && tg > 0 && state->dmrburstL == 26)
{
for (short int k = 0; k < 9; k++)
{
call_matrix[k][0] = call_matrix[k+1][0];
call_matrix[k][1] = call_matrix[k+1][1];
call_matrix[k][2] = call_matrix[k+1][2];
call_matrix[k][3] = call_matrix[k+1][3];
call_matrix[k][4] = call_matrix[k+1][4];
call_matrix[k][5] = call_matrix[k+1][5];
}
call_matrix[9][0] = lls;
call_matrix[9][1] = tg;
call_matrix[9][2] = rd;
call_matrix[9][3] = 0;
call_matrix[9][4] = nc;
call_matrix[9][5] = time(NULL);
//open wav file if enabled and both rd and tg are not 0
if (opts->dmr_stereo_wav == 1 && rd != 0 && tg != 0)
{
//close old first, assign name based on time and radio, open wav file
closeWavOutFileL (opts, state);
sprintf (opts->wav_out_file, "./%s/%s %s P1 - NAC %X - TGT %d - SRC %d.wav", opts->wav_out_dir, getDateN(), getTimeN(), nc, tg, rd);
openWavOutFileL (opts, state); //testing for now, will want to move to per call later
}
if (opts->call_alert == 1)
{
beeper (opts, state, 0);
}
}
//Remus DMR End Call Alert Beep
// if (opts->call_alert == 1)
// {
// if (state->dmrburstL == 2 && state->dmr_end_alert[0] == 0) //if TLC and flag not tripped
// {
// beeper (opts, state, 0);
// state->dmr_end_alert[0] = 1; //don't play again until new voice frames
// state->lasttg = 0;
// state->lastsrc = 0;
// rd = 0;
// tg = 0;
// }
// if (state->dmrburstR == 2 && state->dmr_end_alert[1] == 0) //if TLC and flag not tripped
// {
// beeper (opts, state, 1);
// state->dmr_end_alert[1] = 1; //don't play again until new voice frames
// state->lasttgR = 0;
// state->lastsrcR = 0;
// rdR = 0;
// tgR = 0;
// }
// }
//Start Printing Section
erase();
#ifdef AERO_BUILD
if (opts->ncurses_compact == 1)
{
printw ("------------------------------------------------------------------------------\n");
printw ("| Digital Speech Decoder: Florida Man Edition - Aero %s \n", "AW (20230729)");
printw ("------------------------------------------------------------------------------\n");
}
#elif LIMAZULUTWEAKS
if (opts->ncurses_compact == 1)
{
printw ("------------------------------------------------------------------------------\n");
printw ("| Digital Speech Decoder: LimaZulu Edition VI: Return of the Zulu \n");
printw ("------------------------------------------------------------------------------\n");
}
#elif ZDEV_BUILD
if (opts->ncurses_compact == 1)
{
printw ("------------------------------------------------------------------------------\n");
printw ("| Digital Speech Decoder: Florida Man Edition - zDEV %s \n", GIT_TAG);
printw ("------------------------------------------------------------------------------\n");
}
#else
if (opts->ncurses_compact == 1)
{
printw ("------------------------------------------------------------------------------\n");
printw ("| Digital Speech Decoder: Florida Man Edition - AW (WIP) %s \n", GIT_TAG);
printw ("------------------------------------------------------------------------------\n");
}
#endif
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 == 4) printw (" MBElib %s", versionstr);
#ifdef AERO_BUILD
if (i == 5) printw (" %s ", "Aero Build");
if (i == 6) printw (" AW (WIP) \n");
#elif ZDEV_BUILD
if (i == 5) printw (" %s ", "AW (WIP)");
if (i == 6) printw (" %s \n", GIT_TAG);
#else
if (i == 5) printw (" %s ", "AW (WIP)");
if (i == 6) printw (" %s \n", GIT_TAG);
#endif
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 Audio Input: %i kHz; %i Channel;\n", opts->pulse_digi_rate_in/1000, opts->pulse_digi_in_channels);
}
if (opts->audio_in_type == 5)
{
printw ("| OSS Audio Input: %i kHz; 1 Channel;\n", SAMPLE_RATE_IN/1000);
}
if (opts->audio_in_type == 4)
{
printw ("| Direct Symbol Bin Input: %s \n", opts->audio_in_dev);
}
if (opts->audio_in_type == 8)
{
printw ("| Direct TCP Input: Port: %d; Sample Rate: %d; \n", opts->tcp_portno, opts->wav_sample_rate);
}
if (opts->audio_in_type == 2)
{
printw ("| Direct WAV File Input: %s; Sample Rate: %d; \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 (" Gain: AGC;");
else
printw (" Gain: %idB;", opts->rtl_gain_value);
printw (" PPM: %i;", opts->rtlsdr_ppm_error);
printw (" SQ: %i;", opts->rtl_squelch_level);
printw (" RMS: %04i;", opts->rtl_rms);
printw (" BW: %i kHz;", opts->rtl_bandwidth);
printw (" FRQ: %i;", opts->rtlsdr_center_freq);
if (opts->rtl_udp_port != 0) printw ("\n| External Tuning on UDP Port: %i", opts->rtl_udp_port);
printw ("\n");
}
if (opts->audio_out_type == 0)
{
printw ("| Pulse 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->floating_point == 1) printw (" FBG: %02.0f%%;", opts->audio_gain*2);
if (opts->audio_gain == 0) printw (" (+/-) Auto");
if (opts->audio_gain > 0) printw (" (+/-) Manual");
// if (state->audio_smoothing == 1 && opts->floating_point == 0) printw (" Smoothing On;"); //only on short
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 (state->audio_smoothing == 1 && opts->floating_point == 0) printw (" Smoothing On;"); //only on short
printw (" \n");
}
if (opts->monitor_input_audio == 1)
{
printw ("| Monitoring Source Audio when Carrier Present and No Sync Detected\n");
}
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_file_raw[0] != 0 && opts->audio_in_type == 0)
{
printw ("| Appending Raw Sig Audio WAV to file %s\n", opts->wav_out_file_raw);
}
if (opts->symbol_out_f) //don't display when not actively capturing
{
printw ("| SymbolC Bin: %s\n", opts->symbol_out_file);
}
if (opts->dmr_stereo_wav == 0 && opts->wav_out_file[0] != 0)
{
printw ("| Decoded WAV: %s\n", opts->wav_out_file);
}
if (opts->dmr_stereo_wav == 1) //opts->wav_out_file[0] != 0 &&
{
printw ("| Per Call - %s\n", opts->wav_out_file);
if (opts->dmr_stereo == 1) printw ("| Per Call - %s\n", opts->wav_out_fileR);
}
if (opts->use_rigctl == 1)
{
printw ("| RIGCTL Remote Control Client Established on Port [%d]\n", opts->rigctlportno);
}
#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
if (opts->reverse_mute == 1)
{
printw ("| Reverse Mute - Muting Unencrypted Voice\n");
}
if (opts->call_alert == 1)
{
printw ("| Call Alert Tone Enabled\n");
}
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 -- tera 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 (opts->scanner_mode == 1)
{
printw ("| Fast Scan Mode Enabled ");
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
}
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
reset = 1;
}
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);
printw ("| Decoding: [%s] \n", opts->output_name);
printw ("| In Level: [%02d%%] \n", level);
if (opts->dmr_stereo == 0)
{
printw ("| Voice Error: [%i][%i]", state->errs, state->errs2);
if (opts->slot1_on == 0) printw (" OFF");
if (opts->slot1_on == 1) printw (" ON");
printw ("\n");
}
if (opts->dmr_stereo == 1)
{
printw ("| Voice Error: [%i][%i] Slot 1 (1)", state->errs, state->errs2);
if (opts->slot1_on == 0) printw (" OFF");
if (opts->slot1_on == 1) printw (" ON");
printw ("\n");
printw ("| Voice Error: [%i][%i] Slot 2 (2)", state->errsR, state->errs2R);
if (opts->slot2_on == 0) printw (" OFF");
if (opts->slot2_on == 1) printw (" ON");
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 (" ");
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
{
// for (i = 0; i < 9; i++)
// printw ("%c", state->m17_dst_csd[i]);
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
{
// for (i = 0; i < 9; i++)
// printw ("%c", state->m17_src_csd[i]);
printw("%s", state->m17_src_str);
}
printw ("\n");
printw ("| ");
//fill in any extra info, like Meta (IV, etc)
if (state->m17_enc == 2)
{
printw (" Scrambler - Type: %d", state->m17_enc_st);
}
if (state->m17_enc == 1)
{
printw (" AES-CTR - IV: ");
//display packed meta as IV
for (i = 0; i < 14; 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] ", tgn);
printw ("SRC: [%5d] ", src);
if (state->nxdn_alias_block_segment[0][0] > 0)
{
printw ("ALIAS: [");
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
printw ("%s", state->nxdn_alias_block_segment[i][j]);
}
}
printw ("]");
}
//Group Name Labels from CSV import
for (int k = 0; k < state->group_tally; k++)
{
if (state->group_array[k].groupNumber == tgn)
{
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 == src)
{
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)
{
printw(" %s ", state->nxdn_call_type);
}
printw ("\n|");
if (state->nxdn_cipher_type > 0)
{
printw (" ALG: [0x%02X] KEY: [0x%02X] ", 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 ("KEY VALUE: [%05lld] ", state->R );
printw ("SEED: [%04llX]", state->payload_miN);
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 ("DES-OFB ");
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 ");
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));
}
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; ", dcc);
// 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; ", dcc);
}
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);
}
}
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);
}
}
}
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 & 0xCF) == 0x40) //4F or CF mask? & 0xCF currently
{
attron(COLOR_PAIR(5));
printw (" **Pr** ");
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 & 0xCF) == 0x40)
{
attron(COLOR_PAIR(1));
printw ("Pr 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 & 0xCF) == 0x40) )
{
attron(COLOR_PAIR(1));
printw ("**tera Pr 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 & 0xCF) == 0x40)
{
attron(COLOR_PAIR(1));
printw ("ALG: 0x%02X KEY: 0x%02X MI: 0x%08X ", 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("DES-OFB");
attron(COLOR_PAIR(3));
}
if (state->payload_algid == 0x82)
{
attron(COLOR_PAIR(1));
printw("Double DES");
attron(COLOR_PAIR(3));
}
if (state->payload_algid == 0x83)
{
attron(COLOR_PAIR(1));
printw("Triple DES");
attron(COLOR_PAIR(3));
}
if (state->payload_algid == 0x85 || state->payload_algid == 0x24)
{
attron(COLOR_PAIR(1));
printw("AES-128");
attron(COLOR_PAIR(3));
}
if (state->payload_algid == 0x84 || state->payload_algid == 0x25 || state->payload_algid == 0x05)
{
attron(COLOR_PAIR(1));
printw("AES-256");
attron(COLOR_PAIR(3));
}
if (state->payload_algid == 0x02)
{
attron(COLOR_PAIR(1));
printw("Hytera Full Encrypt");
attron(COLOR_PAIR(3));
}
//Not always correct, or correct at all, depends on context
//this is already in the call_string anyways
// if(state->dmrburstL == 16 && state->dmr_so == 0x40 && state->R == 0) //0100 0000
// {
// attron(COLOR_PAIR(2));
// printw (" **ENC** ");
// attroff(COLOR_PAIR(2));
// attron(COLOR_PAIR(3));
// }
// if(state->dmrburstL == 16 && state->dmr_so == 0x80)
// {
// attron(COLOR_PAIR(2));
// printw (" **Emergency** ");
// attroff(COLOR_PAIR(2));
// attron(COLOR_PAIR(3));
// }
// if(state->dmrburstL == 16 && state->dmr_so == 0x30) //0010 0000
// {
// attron(COLOR_PAIR(2));
// printw (" **Private Call** ");
// attroff(COLOR_PAIR(2));
// attron(COLOR_PAIR(3));
// }
printw ("\n");
//printw ("| | "); //10 spaces
printw ("| D XTRA | ");
attron(COLOR_PAIR(4));
if(state->dmrburstL == 16) //only during call
{
attron(COLOR_PAIR(4));
//Embedded GPS (not LRRP)
printw ("%s ", state->dmr_embedded_gps[0]);
//Embedded Talker Alias Blocks
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 7; j++)
{
printw ("%s", state->dmr_alias_block_segment[0][i][j]);
}
}
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] //Don't print on MS mode
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 & 0xCF) == 0x40) //4F or CF mask?
{
attron(COLOR_PAIR(5));
printw (" **Pr** ");
attroff(COLOR_PAIR(5));
attron(COLOR_PAIR(3));
}
if(state->dmrburstR == 16 && state->payload_algidR == 0 && state->K > 0 && ((state->dmr_soR & 0xCF) == 0x40) && state->dmr_fidR == 0x10)
{
attron(COLOR_PAIR(1));
printw ("Pr 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 & 0xCF) == 0x40) && state->dmr_fidR == 0x68)
{
attron(COLOR_PAIR(1));
printw ("**tera Pr 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 & 0xCF) == 0x40)
{
attron(COLOR_PAIR(1));
printw ("ALG: 0x%02X KEY: 0x%02X MI: 0x%08X ", 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("DES-OFB");
attron(COLOR_PAIR(3));
}
if (state->payload_algidR == 0x83)
{
attron(COLOR_PAIR(1));
printw("Triple DES");
attron(COLOR_PAIR(3));
}
if (state->payload_algidR == 0x85 || state->payload_algidR == 0x24)
{
attron(COLOR_PAIR(1));
printw("AES-128");
attron(COLOR_PAIR(3));
}
if (state->payload_algidR == 0x84 || state->payload_algidR == 0x25 || state->payload_algidR == 0x05)
{
attron(COLOR_PAIR(1));
printw("AES-256");
attron(COLOR_PAIR(3));
}
if (state->payload_algidR == 0x02)
{
attron(COLOR_PAIR(1));
printw("Hytera Full Encrypt");
attron(COLOR_PAIR(3));
}
//Not always correct, or correct at all, depends on context
//this is already in the call_string anyways
// if(state->dmrburstR == 16 && state->dmr_soR == 0x40 && state->R == 0) //0100 0000
// {
// attron(COLOR_PAIR(2));
// printw (" **ENC** ");
// attroff(COLOR_PAIR(2));
// attron(COLOR_PAIR(3));
// }
// if(state->dmrburstR == 16 && state->dmr_soR == 0x80)
// {
// attron(COLOR_PAIR(2));
// printw (" **Emergency** ");
// attroff(COLOR_PAIR(2));
// attron(COLOR_PAIR(3));
// }
// if(state->dmrburstR == 16 && state->dmr_soR == 0x30) //0010 0000
// {
// attron(COLOR_PAIR(2));
// printw (" **Private Call** ");
// attroff(COLOR_PAIR(2));
// attron(COLOR_PAIR(3));
// }
printw ("\n");
//printw ("| | ");
printw ("| D XTRA | ");
attron(COLOR_PAIR(4));
if(state->dmrburstR == 16) //only during call
{
//Embedded GPS (not LRRP)
attron(COLOR_PAIR(4));
printw ("%s ", state->dmr_embedded_gps[1]);
//Embedded Talker Alias Blocks
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 7; j++)
{
printw ("%s", state->dmr_alias_block_segment[1][i][j]);
}
}
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);
}
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 Control Channel\n");
}
else
{
printw ("| Monitoring Voice Channel - LCN [%02d]\n", state->edacs_tuned_lcn);
//since we are tuned, keep updating the time so it doesn't disappear during call
call_matrix[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++)
{
//shim 443 afs in here for display purposes
int a = (call_matrix[i][3] >> 7) & 0xF;
int fs = call_matrix[i][3] & 0x7F;
printw ("| - LCN [%02d][%.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));
}
//print active calls on corresponding LCN line
if ((i != state->edacs_cc_lcn) && time(NULL) - call_matrix[i][5] < 2)
{
attron (COLOR_PAIR(3));
if (state->ea_mode == 1) printw (" TG [%5lld] SRC [%8lld]", call_matrix[i][2], call_matrix[i][3] );
else printw (" AFS [%3llX][%02d-%03d]", call_matrix[i][3], a, fs );
for (int k = 0; k < state->group_tally; k++)
{
if (state->group_array[k].groupNumber == call_matrix[i][2])
{
printw (" [%s]", state->group_array[k].groupName);
printw ("[%s]", state->group_array[k].groupMode);
}
}
attroff (COLOR_PAIR(3));
}
//print dying or dead calls in red for x seconds longer
if ( (i != state->edacs_cc_lcn) && (time(NULL) - call_matrix[i][5] >= 2) && (time(NULL) - call_matrix[i][5] < 5) )
{
attron (COLOR_PAIR(2));
if (state->ea_mode == 1) printw (" TG [%5lld] SRC [%8lld]", call_matrix[i][2], call_matrix[i][3] );
else printw (" AFS [%3llX][%02d-%03d]", call_matrix[i][3], a, fs );
for (int k = 0; k < state->group_tally; k++)
{
if (state->group_array[k].groupNumber == call_matrix[i][2])
{
printw (" [%s]", state->group_array[k].groupName);
printw ("[%s]", state->group_array[k].groupMode);
}
}
attroff (COLOR_PAIR(2));
}
if (i == state->edacs_tuned_lcn && opts->p25_is_tuned == 1) printw (" **T**"); //asterisk which lcn is opened
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 call history if enabled
if (opts->ncurses_history == 1)
{
attron(COLOR_PAIR(4)); //cyan for history
printw ("--Call History----------------------------------------------------------------\n");
for (short int j = 0; j < 10; j++)
{
//only print if a valid time was assigned to the matrix, and not EDACS/PV, and source is not zero
if ( ((time(NULL) - call_matrix[9-j][5]) < 999999) && call_matrix[9-j][0] != 14 && call_matrix[9-j][0] != 15 && call_matrix[9-j][0] != 37 && call_matrix[9-j][0] != 38 && call_matrix[9-j][2] != 0) //
{
// printw ("| %s ", SyncTypes[call_matrix[9-j][0]]); //sync type
printw ("| ");
printw ("%s ", getDateC(call_matrix[9-j][5]) );
printw ("%s ", getTimeC(call_matrix[9-j][5]) );
if (lls == 28 || lls == 29)
{
if (idas == 0) printw ("RAN [%02lld] ", call_matrix[9-j][1]);
if (idas == 1) printw ("Area [%02lld] ", call_matrix[9-j][1]);
printw ("TG [%5lld] ", call_matrix[9-j][4]);
printw ("RID [%5lld] ", call_matrix[9-j][2]);
}
//dPMR
if (lls == 20 || lls == 21 || lls == 22 || lls == 23 ||lls == 24 || lls == 25 || lls == 26 || lls == 27)
{
printw ("TGT [%8lld] ", call_matrix[9-j][1]);
printw ("SRC [%8lld] ", call_matrix[9-j][2]);
printw ("DCC [%2lld] ", call_matrix[9-j][4]);
}
//P25
if (call_matrix[9-j][0] == 0 || call_matrix[9-j][0] == 1 || call_matrix[9-j][0] == 35 || call_matrix[9-j][0] == 36)
{
printw ("TGT [%8lld] ", call_matrix[9-j][1]);
printw ("SRC [%8lld] ", call_matrix[9-j][2]);
printw ("NAC [0x%03llX] ", call_matrix[9-j][4]);
}
//DMR BS Types
if (call_matrix[9-j][0] == 12 || call_matrix[9-j][0] == 13 || call_matrix[9-j][0] == 10 || call_matrix[9-j][0] == 11 )
{
// printw ("S[%lld] ", call_matrix[9-j][3]); //%d
printw ("TGT [%8lld] ", call_matrix[9-j][1]);
printw ("SRC [%8lld] ", call_matrix[9-j][2]);
printw ("DCC [%02lld] ", call_matrix[9-j][4]);
}
//DMR MS Types
if (call_matrix[9-j][0] == 32 || call_matrix[9-j][0] == 33 || call_matrix[9-j][0] == 34 )
{
// printw ("S[%lld] ", call_matrix[9-j][3]);
printw ("TGT [%8lld] ", call_matrix[9-j][1]);
printw ("SRC [%8lld] ", call_matrix[9-j][2]);
printw ("DCC [%02lld] ", call_matrix[9-j][4]);
}
// #ifdef LIMAZULUTWEAKS
//Group Name Labels from CSV import
for (int k = 0; k < state->group_tally; k++)
{
if (state->group_array[k].groupNumber == call_matrix[9-j][1])
{
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 == call_matrix[9-j][2])
{
attron(COLOR_PAIR(4));
printw ("[%s]", state->group_array[k].groupName);
printw ("[%s] ", state->group_array[k].groupMode);
}
}
// #endif
printw ("\n");
}
//EDACS and ProVoice, outside of timestamp loop
if (call_matrix[j][0] == 14 || call_matrix[j][0] == 15 || call_matrix[j][0] == 37 || call_matrix[j][0] == 38 )
{
if (call_matrix[j][3] != 0)
{
printw ("| ");
printw ("%s ", getDateC(call_matrix[j][5]) );
printw ("%s ", getTimeC(call_matrix[j][5]) );
printw ("LCN [%2lld] ", call_matrix[j][1]);
printw ("Group [%8lld] ", call_matrix[j][2]);
printw ("Source [%8lld] ", call_matrix[j][3]);
//test
for (int k = 0; k < state->group_tally; k++)
{
if (state->group_array[k].groupNumber == call_matrix[j][2])
{
attron(COLOR_PAIR(4));
printw ("[%s] ", state->group_array[k].groupName);
}
else if (state->group_array[k].groupNumber == call_matrix[j][3])
{
attron(COLOR_PAIR(4));
printw ("[%s] ", state->group_array[k].groupName);
}
}
//end test
printw ("\n");
}
}
} //end Call History
//fence bottom
printw ("------------------------------------------------------------------------------\n");
attroff(COLOR_PAIR(4)); //cyan for history
}
refresh();
//keyboard shortcuts - codes same as ascii codes
if (c == 27) //esc key, open menu
{
ncursesMenu (opts, state); //just a quick test
}
//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;
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 == 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;
}
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;
}
}
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.bin", getDateN(), getTimeN());
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 AERO_BUILD
//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
{
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, "-T %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);
}
opts->dmr_stereo_wav = 1;
//catch all in case of no file name set, won't crash or something
sprintf (opts->wav_out_file, "./%s/DSD-FME-T1.wav", opts->wav_out_dir);
sprintf (opts->wav_out_fileR, "./%s/DSD-FME-T2.wav", opts->wav_out_dir);
openWavOutFileL (opts, state);
openWavOutFileR (opts, state);
}
//this one could cause issues, but seems okay
if (c == 112) //'p' key - stop all per call wav files
{
//hope this one doesn't cause random crashing or garbage writing
closeWavOutFile (opts, state);
closeWavOutFileL (opts, state);
closeWavOutFileR (opts, state);
sprintf (opts->wav_out_file, "%s", "");
sprintf (opts->wav_out_fileR, "%s", "");
opts->dmr_stereo_wav = 0;
}
//
#ifdef AERO_BUILD //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++;
//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
//RIGCTL
if (opts->p25_trunk == 1 && opts->use_rigctl == 1) 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 (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++;
//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
//RIGCTL
if (opts->p25_trunk == 1 && opts->use_rigctl == 1) 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 (c == 48) //'0' key, toggle upsampled audio smoothing
{
if (state->audio_smoothing == 1) state->audio_smoothing = 0;
else state->audio_smoothing = 1;
}
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);
}
//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;
}
//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
} //end ncursesPrinter
void ncursesClose ()
{
endwin();
}