Files
dsd-fme/src/dsd_ncurses.c
2023-09-20 03:39:03 -04:00

4150 lines
128 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
*-----------------------------------------------------------------------------*/
#include "dsd.h"
#include "git_ver.h"
#ifdef USE_RTLSDR
#include <rtl-sdr.h>
//use to list out all detected RTL dongles
char vendor[256], product[256], serial[256], userdev[256];
int device_count = 0;
#endif
uint32_t temp_freq = -1;
//struct for checking existence of directory to write to
struct stat st_wav = {0};
int reset = 0;
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 == 8) //UDP Audio
{
if (opts->pulse_digi_out_channels == 2 && opts->floating_point == 1)
udp_socket_blaster (opts, state, 320*4, samp_fs);
if (opts->pulse_digi_out_channels == 1 && opts->floating_point == 1)
udp_socket_blaster (opts, state, 160*4, samp_f);
if (opts->pulse_digi_out_channels == 2 && opts->floating_point == 0)
udp_socket_blaster (opts, state, 320*2, samp_ss);
if (opts->pulse_digi_out_channels == 1 && opts->floating_point == 0)
udp_socket_blaster (opts, state, 160*2, samp_s);
}
else if (opts->audio_out_type == 1) //STDOUT
{
if (opts->pulse_digi_out_channels == 2 && opts->floating_point == 1)
write(opts->audio_out_fd, samp_fs, 320*4);
if (opts->pulse_digi_out_channels == 1 && opts->floating_point == 1)
write(opts->audio_out_fd, samp_f, 160*4);
if (opts->pulse_digi_out_channels == 2 && opts->floating_point == 0)
write(opts->audio_out_fd, samp_ss, 320*2);
if (opts->pulse_digi_out_channels == 1 && opts->floating_point == 0)
write(opts->audio_out_fd, samp_s, 160*2);
}
else if (opts->audio_out_type == 2) //OSS Variable Output (no float)
{
if (opts->pulse_digi_out_channels == 2 && opts->floating_point == 0)
write(opts->audio_out_fd, samp_ss, 320*2);
if (opts->pulse_digi_out_channels == 1 && opts->floating_point == 0)
write(opts->audio_out_fd, samp_s, 160*2);
}
else if (opts->audio_out_type == 5) //OSS 48k/1 configuration with upsample
{
short prev = 0;
for (i = 0; i < 160; i++)
{
upsampleS (samp_s[i], prev, outbuf);
samp_su[(i*6)+0] = outbuf[0];
samp_su[(i*6)+1] = outbuf[1];
samp_su[(i*6)+2] = outbuf[2];
samp_su[(i*6)+3] = outbuf[3];
samp_su[(i*6)+4] = outbuf[4];
samp_su[(i*6)+5] = outbuf[5];
}
write (opts->audio_out_fd, samp_su, 960*2);
}
}
}
char * getDateN(void) {
#ifdef AERO_BUILD
char datename[80];
#else
char datename[99];
#endif
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;
}
//this version is broken now in Cygwin with the 80 value, reverted back
char * getDateC(time_t t) {
char datename[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",
"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)
{
//this is primarily used to push a quick audio blip through OSS so it will show up in the mixer immediately
// if (opts->audio_out_type == 2 || opts->audio_out_type == 5)
// beeper (opts, state, 0); //causes crash in Cygwin when mixed input/output
UNUSED(opts); UNUSED(state);
// state->menuopen = 1; //flag the menu is open, stop processing getFrameSync
mbe_printVersion (versionstr);
setlocale(LC_ALL, "");
initscr(); //Initialize NCURSES screen window
start_color();
#ifdef PRETTY_COLORS
init_pair(1, COLOR_YELLOW, COLOR_BLACK); //Yellow/Amber for frame sync/control channel, NV style
init_pair(2, COLOR_RED, COLOR_BLACK); //Red for Terminated Calls
init_pair(3, COLOR_GREEN, COLOR_BLACK); //Green for Active Calls
init_pair(4, COLOR_CYAN, COLOR_BLACK); //Cyan for Site Extra and Patches
init_pair(5, COLOR_MAGENTA, COLOR_BLACK); //Magenta for no frame sync/signal
init_pair(6, COLOR_WHITE, COLOR_BLACK); //White Card Color Scheme
#else
init_pair(1, COLOR_WHITE, COLOR_BLACK); //White Card Color Scheme
init_pair(2, COLOR_WHITE, COLOR_BLACK); //White Card Color Scheme
init_pair(3, COLOR_WHITE, COLOR_BLACK); //White Card Color Scheme
init_pair(4, COLOR_WHITE, COLOR_BLACK); //White Card Color Scheme
init_pair(5, COLOR_WHITE, COLOR_BLACK); //White Card Color Scheme
init_pair(6, COLOR_WHITE, COLOR_BLACK); //White Card Color Scheme
#endif
noecho();
cbreak();
}
//ncursesMenu
void ncursesMenu (dsd_opts * opts, dsd_state * state)
{
//update sync time on cc sync so we don't immediately go CC hunting when exiting the menu
state->last_cc_sync_time = time(NULL);
//close pulse output if not null output
if (opts->audio_out == 1 && opts->audio_out_type == 0)
{
closePulseOutput (opts);
}
//close OSS output
if (opts->audio_out_type == 2 || opts->audio_out_type == 5)
{
close (opts->audio_out_fd);
}
if (opts->audio_in_type == 0) //close pulse input if it is the specified input method
{
closePulseInput(opts);
}
if (opts->audio_in_type == 3)
{
#ifdef USE_RTLSDR
rtl_clean_queue();
#endif
}
if (opts->audio_in_type == 8) //close TCP input SF file so we don't buffer audio while not decoding
{
sf_close(opts->tcp_file_in);
}
state->payload_keyid = 0;
state->payload_keyidR = 0;
//zero out 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, " RTL Device Index or Serial Number:");
mvwprintw(entry_win, 3, 3, " ");
echo();
refresh();
wscanw(entry_win, "%d", &opts->rtl_dev_index);
noecho();
entry_win = newwin(6, WIDTH+6, starty+10, startx+10);
box (entry_win, 0, 0);
mvwprintw(entry_win, 2, 2, " RTL Device PPM Error:");
mvwprintw(entry_win, 3, 3, " ");
echo();
refresh();
wscanw(entry_win, "%d", &opts->rtlsdr_ppm_error);
noecho();
entry_win = newwin(6, WIDTH+18, starty+10, startx+10);
box (entry_win, 0, 0);
mvwprintw(entry_win, 2, 2, " Frequency in Hz (851.8 MHz is 851800000 Hz): ");
mvwprintw(entry_win, 3, 3, " ");
echo();
refresh();
wscanw(entry_win, "%d", &opts->rtlsdr_center_freq);
noecho();
entry_win = newwin(6, WIDTH+18, starty+10, startx+10);
box (entry_win, 0, 0);
mvwprintw(entry_win, 2, 2, " BW (8, 12, 24, 48)(12 Recommended): ");
mvwprintw(entry_win, 3, 3, " ");
echo();
refresh();
wscanw(entry_win, "%d", &opts->rtl_bandwidth);
noecho();
entry_win = newwin(6, WIDTH+18, starty+10, startx+10);
box (entry_win, 0, 0);
mvwprintw(entry_win, 2, 2, " RTL Gain Value (0-49) (0 = AGC): ");
mvwprintw(entry_win, 3, 3, " ");
echo();
refresh();
wscanw(entry_win, "%d", &opts->rtl_gain_value);
noecho();
entry_win = newwin(6, WIDTH+18, starty+10, startx+10);
box (entry_win, 0, 0);
mvwprintw(entry_win, 2, 2, " RTL UDP Port - Legacy/Optional (Default = 0): ");
mvwprintw(entry_win, 3, 3, " ");
echo();
refresh();
wscanw(entry_win, "%d", &opts->rtl_udp_port);
noecho();
entry_win = newwin(8, WIDTH+22, starty+10, startx+10);
box (entry_win, 0, 0);
mvwprintw(entry_win, 2, 2, " RTL RMS Squelch Level (NXDN/dPMR/Analog/Raw only): ");
mvwprintw(entry_win, 3, 3, " ");
echo();
refresh();
wscanw(entry_win, "%d", &opts->rtl_squelch_level);
noecho();
//use to list out all detected RTL dongles
device_count = rtlsdr_get_device_count();
if (!device_count)
{
fprintf(stderr, "No supported devices found.\n");
}
else fprintf(stderr, "Found %d device(s):\n", device_count);
for (int i = 0; i < device_count; i++)
{
rtlsdr_get_device_usb_strings(i, vendor, product, serial);
fprintf(stderr, " %d: %s, %s, SN: %s\n", i, vendor, product, serial);
sprintf (userdev, "%08d", opts->rtl_dev_index);
//check by index first, then by serial
if (opts->rtl_dev_index == i)
{
fprintf (stderr, "Selected Device #%d with Serial Number: %s \n", i, serial);
break;
}
else if (strcmp (userdev, serial) == 0)
{
fprintf (stderr, "Selected Device #%d with Serial Number: %s \n", i, serial);
opts->rtl_dev_index = i;
break;
}
}
entry_win = newwin(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; SN:%s", opts->rtl_dev_index, serial);
mvwprintw(entry_win, 6, 2, " RTL Device Bandwidth: %d kHz", opts->rtl_bandwidth);
mvwprintw(entry_win, 7, 2, " RTL Device Gain: %d", opts->rtl_gain_value);
mvwprintw(entry_win, 8, 2, " RTL 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
}
#else
UNUSED(confirm);
#endif
}
if (choicec == 7) //RTL DEV Retune
{
//read in new rtl frequency
#ifdef USE_RTLSDR
entry_win = newwin(6, WIDTH+18, starty+10, startx+10);
box (entry_win, 0, 0);
mvwprintw(entry_win, 2, 2, " Enter Frequency in Hz (851.8 MHz is 851800000 Hz) ");
mvwprintw(entry_win, 3, 3, " ");
echo();
refresh();
wscanw(entry_win, "%d", &opts->rtlsdr_center_freq);
noecho();
temp_freq = opts->rtlsdr_center_freq;
#endif
choicec = 18;
}
if (choicec == 8)
{
if (state->rf_mod == 0)
{
state->rf_mod = 1;
state->samplesPerSymbol = 8;
state->symbolCenter = 3;
opts->mod_c4fm = 0;
opts->mod_qpsk = 1;
}
else
{
state->rf_mod = 0;
state->samplesPerSymbol = 10;
state->symbolCenter = 4;
opts->mod_c4fm = 1;
opts->mod_qpsk = 0;
}
}
if (choicec == 9)
{
if (state->rf_mod == 0)
{
state->rf_mod = 1;
state->samplesPerSymbol = 10;
state->symbolCenter = 4;
opts->mod_c4fm = 0;
opts->mod_qpsk = 1;
}
else
{
state->rf_mod = 0;
state->samplesPerSymbol = 10;
state->symbolCenter = 4;
opts->mod_c4fm = 1;
opts->mod_qpsk = 0;
}
}
// if (choicec == 9)
// {
// if (opts->audio_out == 0)
// {
// opts->audio_out = 1;
// opts->audio_out_type = 0;
// // state->audio_out_buf_p = 0;
// // state->audio_out_buf_pR = 0;
// state->audio_out_idx = 0;
// state->audio_out_idx2 = 0;
// state->audio_out_idxR = 0;
// state->audio_out_idx2R = 0;
// }
// else
// {
// opts->audio_out = 0;
// opts->audio_out_type = 9;
// // state->audio_out_buf_p = 0;
// // state->audio_out_buf_pR = 0;
// state->audio_out_idx = 0;
// state->audio_out_idx2 = 0;
// state->audio_out_idxR = 0;
// state->audio_out_idx2R = 0;
// }
// }
//TCP Audio Input
if (choicec == 10)
{
//read in tcp hostname
sprintf (opts->tcp_hostname, "%s", "localhost");
opts->tcp_portno = 7355;
entry_win = newwin(8, WIDTH+16, starty+10, startx+10);
box (entry_win, 0, 0);
mvwprintw(entry_win, 2, 2, " Enter TCP Direct Link Hostname:");
mvwprintw(entry_win, 3, 2, " (default is localhost)");
mvwprintw(entry_win, 4, 2, " ");
mvwprintw(entry_win, 5, 3, " ");
echo();
refresh();
wscanw(entry_win, "%s", opts->tcp_hostname);
noecho();
//read in tcp port number
entry_win = newwin(8, WIDTH+16, starty+10, startx+10);
box (entry_win, 0, 0);
mvwprintw(entry_win, 2, 2, " Enter TCP Direct Link Port Number:");
mvwprintw(entry_win, 3, 2, " (default is 7355)");
mvwprintw(entry_win, 4, 2, " ");
mvwprintw(entry_win, 5, 3, " ");
echo();
refresh();
wscanw(entry_win, "%d", &opts->tcp_portno);
noecho();
opts->tcp_sockfd = Connect(opts->tcp_hostname, opts->tcp_portno);
if (opts->tcp_sockfd == 0) goto TCP_END;
//successful connection, continue;
opts->audio_in_type = 8;
opts->audio_in_file_info = calloc(1, sizeof(SF_INFO));
opts->audio_in_file_info->samplerate=opts->wav_sample_rate;
opts->audio_in_file_info->channels=1;
opts->audio_in_file_info->seekable=0;
opts->audio_in_file_info->format=SF_FORMAT_RAW|SF_FORMAT_PCM_16|SF_ENDIAN_LITTLE;
opts->tcp_file_in = sf_open_fd(opts->tcp_sockfd, SFM_READ, opts->audio_in_file_info, 0);
if(opts->tcp_file_in == NULL)
{
fprintf(stderr, "Error, couldn't open TCP with libsndfile: %s\n", sf_strerror(NULL));
if (opts->audio_out_type == 0)
{
sprintf (opts->audio_in_dev, "%s", "pulse");
opts->audio_in_type = 0;
}
else opts->audio_in_type = 5;
}
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);
}
//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 != 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 (20230915)");
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 %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 && opts->frame_m17 == 0) printw (" MBElib %s", versionstr);
#ifdef USE_CODEC2
if (i == 4 && opts->frame_m17 == 1) printw (" CODEC2");
#endif
#ifdef AERO_BUILD
if (i == 5) printw (" %s ", "Aero Build");
if (i == 6) printw (" AW (20230915) \n");
#elif ZDEV_BUILD
if (i == 5) printw (" %s ", "AW ");
if (i == 6) printw (" %s \n", GIT_TAG);
#else
if (i == 5) printw (" %s ", "AW ");
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; ", opts->pulse_digi_rate_in/1000, opts->pulse_digi_in_channels);
if (opts->use_rigctl == 1)
printw ("RIG: %s:%d; ", opts->tcp_hostname, opts->rigctlportno);
printw ("\n");
}
if (opts->audio_in_type == 5)
{
printw ("| OSS Audio Input: %i kHz; 1 Channel;", SAMPLE_RATE_IN/1000);
if (opts->use_rigctl == 1)
printw ("RIG: %s:%d; ", opts->tcp_hostname, opts->rigctlportno);
printw ("\n");
}
if (opts->audio_in_type == 4)
{
printw ("| Symbol Bin Input: %s \n", opts->audio_in_dev);
}
if (opts->audio_in_type == 8)
{
printw ("| TCP Audio Input: %s:%d; %d kHz 1 Channel; ", opts->tcp_hostname, opts->tcp_portno, opts->wav_sample_rate/1000);
if (opts->use_rigctl == 1)
printw ("RIG: %s:%d; ", opts->tcp_hostname, opts->rigctlportno);
printw ("\n");
}
if (opts->audio_in_type == 2)
{
printw ("| WAV Audio Input: %s; %d kHz; \n", opts->audio_in_dev, opts->wav_sample_rate);
}
if (opts->audio_in_type == 1)
{
printw ("| STDIN Standard Input: - Menu Disabled when using STDIN!\n");
// printw ("| NCURSES Menu Disabled when using STDIN! - Use CTRL + C to Close. \n");
}
if (opts->audio_in_type == 3)
{
printw ("| RTL: %d;", opts->rtl_dev_index);
if (opts->rtl_gain_value == 0)
printw (" 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: %04li;", 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 RTL 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 (" FLOAT: %02.0f%%;", opts->audio_gain*2);
if (opts->audio_gain == 0) printw (" (+/-) Auto");
if (opts->audio_gain > 0) printw (" (+/-) Manual");
if (opts->call_alert == 1) printw (" *CA!"); //Call Alert
// if (state->audio_smoothing == 1 && opts->floating_point == 0) printw (" Smoothing On;"); //only on short
printw (" \n");
}
if ( opts->audio_out_type == 0 && (opts->frame_provoice == 1 || opts->monitor_input_audio == 1) )
{
printw ("| Pulse Audio Output: %i kHz; %i Ch; Analog Monitor RMS: %04ld ", opts->pulse_raw_rate_out/1000, opts->pulse_raw_out_channels, opts->rtl_rms);
printw (" \n");
}
if (opts->audio_out_type == 5 || opts->audio_out_type == 2)
{
printw ("| OSS Audio Output: %i kHz; %i Ch; G: %02.0f%%", opts->pulse_digi_rate_out/1000, opts->pulse_digi_out_channels, state->aout_gain*2);
if (opts->pulse_digi_out_channels == 2) printw (" G: %02.0f%%", state->aout_gainR*2);
if (opts->audio_gain == 0) printw (" (+/-) Auto");
if (opts->audio_gain > 0) printw (" (+/-) Manual");
if (opts->call_alert == 1) printw (" *CA!"); //Call Alert
// if (state->audio_smoothing == 1 && opts->floating_point == 0) printw (" Smoothing On;"); //only on short
if ( (opts->audio_out_type == 5 && opts->pulse_digi_rate_out == 48000 && opts->pulse_digi_out_channels == 1) && (opts->frame_provoice == 1 || opts->monitor_input_audio == 1) )
printw (" - Monitor RMS: %04ld ", opts->rtl_rms);
printw (" \n");
}
if (opts->audio_out_type == 8)
{
printw ("| UDP Audio Output: %s:%d; %d kHz %d Ch; %02.0f%%", opts->udp_hostname, opts->udp_portno, opts->pulse_digi_rate_out/1000, opts->pulse_digi_out_channels, state->aout_gain*2);
if (opts->pulse_digi_out_channels == 2) printw (" G: %02.0f%%", state->aout_gainR*2);
if (opts->audio_gain == 0) printw (" (+/-) Auto");
if (opts->audio_gain > 0) printw (" (+/-) Manual");
if (opts->call_alert == 1) printw (" *CA!"); //Call Alert
if ( (opts->audio_out_type == 5 && opts->pulse_digi_rate_out == 48000 && opts->pulse_digi_out_channels == 1) && (opts->frame_provoice == 1 || opts->monitor_input_audio == 1) )
printw (" - Monitor RMS: %04ld ", opts->rtl_rms);
printw (" \n");
}
// if (opts->monitor_input_audio == 1)
// {
// printw ("| Monitoring Source Audio when Carrier Present and No Sync Detected (WIP)\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_raw)
{
printw ("| Raw Audio Output: %s\n", opts->wav_out_file_raw);
}
if (opts->symbol_out_f) //don't display when not actively capturing
{
printw ("| Symbol Bin Output: %s\n", opts->symbol_out_file);
}
if (opts->dmr_stereo_wav == 0 && opts->wav_out_file[0] != 0)
{
printw ("| Decoded WAV Output: %s\n", opts->wav_out_file);
}
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);
}
#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->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: ");
if (state->lcn_freq_roll != 0)
printw (" Frequency: %.06lf Mhz", (double)state->trunk_lcn_freq[state->lcn_freq_roll-1]/1000000);
printw (" Speed: %.02lf sec \n", opts->trunk_hangtime); //not sure values less than 1 make a difference, may be system/environment dependent
}
if (opts->reverse_mute == 1) printw ("| Reverse Mute - Muting Unencrypted Voice\n");
// if (opts->call_alert == 1) printw ("| Call Alert Tone Enabled\n");
printw ("------------------------------------------------------------------------------\n");
attroff(COLOR_PAIR(4));
if (state->carrier == 1)
{
attron(COLOR_PAIR(3));
level = (int) state->max / 164; //only update on carrier present
if (opts->mod_qpsk == 1) level = (int) state->max / 328; //test values here
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] ", opts->output_name);
if (opts->aggressive_framesync == 0) printw ("CRC/(RAS) ");
//debug -- troubleshoot voice tuning after grant on DMR CC, subsequent grant may not tune because tuner isn't available
if (opts->p25_trunk == 1 && opts->p25_is_tuned == 1) printw ("Tuner Locked ");
if (opts->p25_trunk == 1 && opts->p25_is_tuned == 0) printw ("Tuner Available ");
printw ("\n");
printw ("| In Level: [%02d%%] \n", level);
if (opts->dmr_stereo == 0)
{
printw ("| Voice Error: [%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 ("\n");
printw ("| ");
printw ("DST: ");
if (state->m17_dst == 0xFFFFFFFFFFFF)
printw("BROADCAST ");
else if (state->m17_dst != 0 && state->m17_dst >= 0xEE6B28000000)
printw("RESERVED (%012llx) ", state->m17_dst);
else
printw("%s", state->m17_dst_str);
printw ("\n");
printw ("| ");
printw ("SRC: ");
if (state->m17_src != 0 && state->m17_src >= 0xEE6B28000000)
printw("RESERVED (%012llx)", state->m17_src);
else
printw("%s", state->m17_src_str);
printw ("\n");
printw ("| ");
printw ("CAN: %02d ", state->m17_can);
printw ("\n");
printw ("| ");
//fill in any extra info, like Meta (IV, etc)
if (state->m17_enc == 1)
{
printw (" Scrambler - Type: %d", state->m17_enc_st);
}
if (state->m17_enc == 2)
{
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 ("DES1 ");
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("DES1 ");
attron(COLOR_PAIR(3));
}
if (state->payload_algid == 0x82)
{
attron(COLOR_PAIR(1));
printw("DES2 ");
attron(COLOR_PAIR(3));
}
if (state->payload_algid == 0x83)
{
attron(COLOR_PAIR(1));
printw("DES3 ");
attron(COLOR_PAIR(3));
}
if (state->payload_algid == 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("DES1 ");
attron(COLOR_PAIR(3));
}
if (state->payload_algidR == 0x82)
{
attron(COLOR_PAIR(1));
printw("DES2 ");
attron(COLOR_PAIR(3));
}
if (state->payload_algidR == 0x83)
{
attron(COLOR_PAIR(1));
printw("DES3 ");
attron(COLOR_PAIR(3));
}
if (state->payload_algidR == 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);
}
//TG Hold, if specified by user
if (state->tg_hold != 0) printw ("TG HOLD: %d", state->tg_hold);
if (state->carrier == 1) attron(COLOR_PAIR(3));
else attroff(COLOR_PAIR(4));
printw ("\n");
}
} // end if not MS
} //end DMR BS Types
//dPMR
if (lls == 20 || lls == 21 || lls == 22 || lls == 23 ||lls == 24 || lls == 25 || lls == 26 || lls == 27)
{
printw ("| DCC: [%i] ", state->dpmr_color_code);
printw ("TGT: [%s] SRC: [%s] ", state->dpmr_target_id, state->dpmr_caller_id);
printw ("\n| ");
if (state->dPMRVoiceFS2Frame.Version[0] == 3)
{
attron(COLOR_PAIR(2));
printw ("Scrambler ");
attroff(COLOR_PAIR(2));
attron(COLOR_PAIR(3));
if (state->R != 0)
{
attron(COLOR_PAIR(1));
printw ("KEY VALUE: [%05lld] ", state->R );
//printw ("SEED: [%04llX]", state->payload_miN);
attron(COLOR_PAIR(3));
}
}
printw ("\n");
}
//EDACS and ProVoice
if (lls == 14 || lls == 15 || lls == 37 || lls == 38)
{
attroff (COLOR_PAIR(3)); //colors off for EDACS
if (state->edacs_site_id != 0)
{
if (opts->p25_is_tuned == 0)
{
printw ("| Monitoring 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
}
//use k and l keys to test tg hold toggles on slots 1 and slots 2
if (c == 107) //'k' key, hold tg on slot 1 for trunking purposes, or toggle clear
{
if (state->tg_hold == 0)
state->tg_hold = state->lasttg;
else state->tg_hold = 0;
}
if (c == 108) //'l' key, hold tg on slot 2 for trunking purposes, or toggle clear
{
if (state->tg_hold == 0)
state->tg_hold = state->lasttgR;
else state->tg_hold = 0;
}
//toggling when 48k/1 OSS still has some lag -- needed to clear out the buffer when switching
if (c == 49) // '1' key, toggle slot1 on
{
//switching, but want to control each seperately plz
if (opts->slot1_on == 1)
{
opts->slot1_on = 0;
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;
opts->audio_gainR = opts->audio_gain;
}
if (c == 45) //- key, decrement audio_gain
{
if (opts->audio_gain > 0)
opts->audio_gain--;
state->aout_gain = opts->audio_gain;
state->aout_gainR = opts->audio_gain;
//reset to default on 0 for auto
if (opts->audio_gain == 0)
{
state->aout_gain = 25;
state->aout_gainR = 25;
}
opts->audio_gainR = opts->audio_gain;
}
if (c == 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 P25p2 VCH and going back to P25p1 CC, flip symbolrate
if (state->p25_cc_is_tdma == 0)
{
state->samplesPerSymbol = 10;
state->symbolCenter = 4;
}
}
if (state->lasttgR != 0 && opts->frame_provoice != 1 && c == 64) //SHIFT+'2' key (@ at sign), lockout slot 2 tdma tgR from tuning/playback during session
{
state->group_array[state->group_tally].groupNumber = state->lasttgR;
sprintf (state->group_array[state->group_tally].groupMode, "%s", "B");
sprintf (state->group_array[state->group_tally].groupName, "%s", "LOCKOUT");
state->group_tally++;
//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 P25p2 VCH and going back to P25p1 CC, flip symbolrate
if (state->p25_cc_is_tdma == 0)
{
state->samplesPerSymbol = 10;
state->symbolCenter = 4;
}
}
// 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();
}