Compare commits

...
Sign in to create a new pull request.

3 commits
master ... dev

Author SHA1 Message Date
hornet
c30e48824e added DEFCON level parsing capability, with appealing output 2025-01-02 01:09:37 +05:00
hornet
828fc7d4ed updoot readme 2025-01-01 22:18:28 +05:00
hornet
807e3d0465 add makefile, create ncurses windows 2025-01-01 22:16:44 +05:00
8 changed files with 373 additions and 67 deletions

4
Makefile Normal file
View file

@ -0,0 +1,4 @@
all: sentinel
sentinel: main.c
gcc -lncurses -lcurl -lrtlsdr -lm main.c -o sentinel

View file

@ -10,9 +10,12 @@ mostly by scrapping API endpoints, gathering RSS feeds and getting readings from
but of course! if you wish to add support for your SDR or generally help me make this idea become true - go ahead! you can contact me at [hornet@lainlounge.xyz](mailto:hornet@lainlounge.xyz) via email or XMPP
## installation
WIP
this is a C program, so everything here will be around compiling stuff with gcc, or maybe i'll learn how to use make properly
```
git clone https://git.lainlounge.xyz/hornet/sentinel
cd sentinel
make
./sentinel
```
## roadmap

25
ascii/level1 Normal file
View file

@ -0,0 +1,25 @@
MWMMMWWWWMWMWWMMMMMMWWMWWNMMWWMMMMMMMWMMMM
MWWWWWMMWMMWWWWWN0XNXNNNNKXKXWMMMWMMMMMMWW
MMMMMWMMMWMWKN0Kxccloxddlckxkk0XWMWMMWWWWM
WMMWMWMMMWWOcl,:. . .' 'dO0XWWMMWWW
MMMWMMMWWNO' .. .,dOKNMMMW
WWWWMWMMWd. ' .'kWWMW
WWMMWMWW0. . , .kWWW
MMMMMMMW: ; .. . . .KWM
MMMWMMWK' ;.:'d '. . cWM
MMMMMMM0. .:ol,'.;o0ko. . dx:'....xM
MWWMMMMW, ; . .'K.;lKNWWWN: . NWNXOdcllW
MWMMWMMMN;.c'; .'K:KXWWWMWo' ..kOWWWKoocM
WMMWMMMMMWcl:,': .O;llOKOkl,.:,dxlodlcol:M
MWWWMMMMMWW,kc.x.,OlO'locod..k'0kococlxldM
MWMMMMMMWWMoN,.d.;X:x0'xd.. ;l.xl:,.dkOOXW
MMWMMMMMMWWdO;.c.'d.lNdoK; .; l,..'XNWWWM
MMMMMMWWMMMdK. ,..; .XlxNO;..:.:;',oWMMWMM
MWWMWMWWMWMoO. '. oolOc',;,cc;'':WMMMWM
MWMWWWMWWWWlWc . .';,..',o,.. ;MMWWWW
MMWMWMMMMMXlN;; 'c:.. ...'..'0MMMWMM
MMWMMMMMWXd.ddx lok;.,..:klxOWMMWMWW
WMWMMWNNxl.'Ndl . .. .. . .d;xxKXXWWWM
NXXX0kkk. 'O,, :d. . . . ....:kxk0k0
dxc;. .. . . oxkl . . ...'':
' . . :0K' . .. ..

23
ascii/level2 Normal file
View file

@ -0,0 +1,23 @@
................
.... .'.
'' ...
;. .,
.. .;
: ;
.; ;
'. .,
'. .,;;. c
: kNdWd 0K00 .;
.. ' ... .,,. :
,. , .; ''
., : l' ,c.
: .: :,;.. . . .;l.
; ., ::;;,,.. .cc:.
; .' .;:c;''','.,.;l:;
: .'..,;:;;,,;,;c'.,
:. '......,'...;
;. .....;...
..;; .'........
..... ...

23
ascii/level3 Normal file
View file

@ -0,0 +1,23 @@
..............
..... ..'.
,' ........... .''
: .. ',
,. ........ .;
: ..... ;.
,. '. .. .. .,;
; '. .. ''
; c:,,,,,,,;kcllo:,;c'''..c,;;,:x
.',' .d''kNNo.,x..,xddNk .O;
:. .d;'.....o; .d;',. .0:
:. ..,o ....... ,;'';;,:l.
,. 'x. .. .: ..k.
: .k; .;. . .;, ..o,
; ,Oc .' ,;:;,,;.. 'c,
: co: .' XNWWWXXWO..o.
,. .'ok. KMMMMMMW;.x:
: lol. .0MMMNk':k,
.: ,. :dol..:l:'.oo.
...;. ., ,;cx':,ll'........
... .. .
...

27
ascii/level4 Normal file
View file

@ -0,0 +1,27 @@
.:lcccccccl:;.
.'''. .''.
;'. .''
., ',.
c .;
o ......... .. .,
.. ...... .. c
c ..... ...... .:
l ....... ..... ..
: . ' c
: .d'XXx'. '.ld: '
' .'ool'. .;.O0c o
. , ..
. c '; '
. c .. :. .
; o c. .. ..c .
; .: . .
: , ', '........ ,
.; ' .,' .. .
c ,,
:. .. '''.. .
.:. :. ....l.
.'':; ., . '.......''..
.... .. ..
. .

23
ascii/level5 Normal file
View file

@ -0,0 +1,23 @@
.,;cloodddoolc:;'...
.coxMMMMNOOO000000WMMMMMMWNKOd.
OMXo'.. ..,'.cMl
:Mc 'MW
NM. .. . ,MM.
.MM. ;:'. . . ...,o ;
...Md ;0XWMNKx;.. .'cd0XWW0d'. '
.. ; ..; ,:x:cl;.....'c;lox0;,.. ..
.: ...... , .. c. ...., ;
, .. ' ,........ '
' ' .. ..
' , ' '
.. .......' '
.. . ..
' .. ............ .. ,
' .:............,, '
.. . .. ... ..'
' ........ ..
... .. .. ..
.. ...
.......

306
main.c
View file

@ -3,8 +3,10 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <ncurses.h>
#include <rtl-sdr.h>
#include <curl/curl.h>
@ -34,6 +36,18 @@
#define M_PI 3.14159265358979323846
/*define DEFCON level stuff */
#define DEFCON_URL "https://defconwarningsystem.com/code.dat"
unsigned int level;
unsigned int cooldown = 1800;
unsigned int cooldown_timer = 0;
bool cooldown_reset = false;
size_t write_data(void *ptr1, size_t size, size_t nmemb, FILE *stream) {
size_t written = fwrite(ptr1, size, nmemb, stream);
return written;
};
/* declare global variables */
rtlsdr_dev_t *dev = NULL;
@ -46,9 +60,38 @@ bool SDR_PRESENT = false;
bool RSS_LIST_PRESENT = false;
bool COMMS_AVAILABLE = false;
bool CURL_AVAILABLE = false;
int term_rows, term_cols;
/* declare ncurses windows */
WINDOW * sdr;
WINDOW * rss;
WINDOW * defcon;
/* startup screen! :3 */
void startup_screen() {
printf("\n\n");
printf(CYAN" █████ ███ ████ \n");
printf(" ░░███ ░░░ ░░███ \n");
printf(" █████ ██████ ████████ ███████ ████ ████████ ██████ ░███ \n");
printf(" ███░░ ███░░███░░███░░███ ░░░███░ ░░███ ░░███░░███ ███░░███ ░███ \n");
printf("░░█████ ░███████ ░███ ░███ ░███ ░███ ░███ ░███ ░███████ ░███ \n");
printf(" ░░░░███░███░░░ ░███ ░███ ░███ ███ ░███ ░███ ░███ ░███░░░ ░███ \n");
printf(" ██████ ░░██████ ████ █████ ░░█████ █████ ████ █████░░██████ █████\n");
printf("░░░░░░ ░░░░░░ ░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░ ░░░░░ ░░░░░░ ░░░░░ \n\n");
printf(" v0.0.1\n" RESET);
}
/* declare reusable functions */
void get_terminal_size(int *rows, int *cols) {
struct winsize w;
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
*rows = w.ws_row;
*cols = w.ws_col;
}
/* read out shell command output */
char* readout(const char* command, char* buffer, size_t size) {
FILE *fp = popen(command, "r");
@ -67,19 +110,6 @@ char* readout(const char* command, char* buffer, size_t size) {
return buffer;
}
void startup_screen() {
printf("\n\n");
printf(CYAN" █████ ███ ████ \n");
printf(" ░░███ ░░░ ░░███ \n");
printf(" █████ ██████ ████████ ███████ ████ ████████ ██████ ░███ \n");
printf(" ███░░ ███░░███░░███░░███ ░░░███░ ░░███ ░░███░░███ ███░░███ ░███ \n");
printf("░░█████ ░███████ ░███ ░███ ░███ ░███ ░███ ░███ ░███████ ░███ \n");
printf(" ░░░░███░███░░░ ░███ ░███ ░███ ███ ░███ ░███ ░███ ░███░░░ ░███ \n");
printf(" ██████ ░░██████ ████ █████ ░░█████ █████ ████ █████░░██████ █████\n");
printf("░░░░░░ ░░░░░░ ░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░ ░░░░░ ░░░░░░ ░░░░░ \n\n");
printf(" v0.0.1\n" RESET);
}
/* declare functions for startup checks */
void util_check() {
printf("curl: ");
@ -127,8 +157,8 @@ void defcon_level_check() {
fputc(5, fp);
fclose(fp);
} else {
int last_known = fgetc(fp);
printf("DEFCON level cache file found! last known level: %d\n", last_known);
int last_known_startup = fgetc(fp);
printf("DEFCON level cache file found! last known level: %d\n", last_known_startup);
fclose(fp);
}
}
@ -164,6 +194,7 @@ void rss_sources_check() {
/* making sure program has everything it needs to run, more checks will be introduced as capabilities are added */
void startup_checks() {
printf("######################################################################\n");
printf(MAGENTA"STARTUP CHECKS\n"RESET);
@ -180,9 +211,13 @@ void startup_checks() {
} else {
printf("internet connectivity status: ");
printf(RED"offline\n"RESET);
printf(RED"internet-dependent capabilities will be unavailable!"RESET);
printf(RED"internet-dependent capabilities will be unavailable!\n"RESET);
COMMS_AVAILABLE = false;
}
/* get terminal size */
get_terminal_size(&term_rows, &term_cols);
printf("terminal size: ");
printf(YELLOW"%dx%d\n"RESET, term_rows, term_cols);
printf("----------------------------------------------------------------------\n");
/* utilities presence check */
printf(MAGENTA"UTILITIES\n\n"RESET);
@ -200,7 +235,7 @@ void startup_checks() {
printf(MAGENTA"all checks finished!\n"RESET);
printf("######################################################################\n");
printf(BLUE"initializing in 5 seconds...\n\n"RESET);
usleep(5000000);
//usleep(5000000);
if (SDR_PRESENT == true)
{
printf(GREEN"initializing RTL-SDR...\n"RESET);
@ -209,9 +244,38 @@ void startup_checks() {
if (RSS_LIST_PRESENT == true && COMMS_AVAILABLE == true && CURL_AVAILABLE == true) {
printf(GREEN"initializing RSS aggregator...\n"RESET);
}
if (COMMS_AVAILABLE == true && CURL_AVAILABLE == true) {
printf(GREEN"initializing DEFCON level parser...\n"RESET);
}
}
/* define functions for dashboard - WIP */
void init_colors() {
start_color();
init_pair(1, COLOR_RED, COLOR_BLACK);
init_pair(2, COLOR_GREEN, COLOR_BLACK);
init_pair(3, COLOR_YELLOW, COLOR_BLACK);
init_pair(4, COLOR_BLUE, COLOR_BLACK);
init_pair(5, COLOR_MAGENTA, COLOR_BLACK);
init_pair(6, COLOR_CYAN, COLOR_BLACK);
init_pair(7, COLOR_WHITE, COLOR_BLACK);
init_pair(8, COLOR_WHITE, COLOR_RED);
init_pair(9, COLOR_WHITE, COLOR_GREEN);
}
void print_centered(WINDOW *win, const char *text) {
int width = getmaxx(win);
int height = getmaxy(win)/2;
int length = strlen(text);
int start_col = (width - length) / 2;
mvwprintw(win, height, start_col, "%s", text);
}
/* define rtl-sdr specific functions and general SDR workflow - WIP */
/* signal analysis function */
void fourier_transform(unsigned char *buf, double *out, int n) {
for (int k = 0; k < n; k++) {
@ -226,25 +290,12 @@ void fourier_transform(unsigned char *buf, double *out, int n) {
}
}
/* define functions for dashboard - WIP */
void init_colors() {
start_color();
init_pair(1, COLOR_RED, COLOR_BLACK);
init_pair(2, COLOR_GREEN, COLOR_BLACK);
init_pair(3, COLOR_YELLOW, COLOR_BLACK);
init_pair(4, COLOR_BLUE, COLOR_BLACK);
init_pair(5, COLOR_MAGENTA, COLOR_BLACK);
init_pair(6, COLOR_CYAN, COLOR_BLACK);
init_pair(7, COLOR_WHITE, COLOR_BLACK);
}
void draw_bar_graph(double *data, int size) {
clear();
wclear(sdr);
double max_value = 0.0;
attron(COLOR_PAIR(6));
printw("center frequency: %.3f MHz gain: %d bandwidth: %.3f MHz", center_freq / (double)MHz, rtlsdr_get_tuner_gain(dev), bandwidth / (double)MHz);
attroff(COLOR_PAIR(6));
wattron(sdr, COLOR_PAIR(6));
wprintw(sdr, "center frequency: %.3f MHz gain: %d bandwidth: %.3f MHz", center_freq / (double)MHz, rtlsdr_get_tuner_gain(dev), bandwidth / (double)MHz);
wattroff(sdr, COLOR_PAIR(6));
double bottom = center_freq - bandwidth / 2.0;
double step = bandwidth / (double)size;
for (int i = 0; i < size; i++) {
@ -252,20 +303,17 @@ void draw_bar_graph(double *data, int size) {
max_value = data[i];
}
}
attron(COLOR_PAIR(4));
wattron(sdr, COLOR_PAIR(4));
for (int i = 0; i < size; i++) {
mvprintw(i + 1, 0, "%.3f MHz: ", bottom / (double)MHz + i * step / (double)MHz);
mvwprintw(sdr, i + 1, 0, "%.3f MHz: ", bottom / (double)MHz + i * step / (double)MHz);
for (int j = 0; j < data[i] * 50 / max_value; j++) {
printw(">");
wprintw(sdr, ">");
}
}
attroff(COLOR_PAIR(4));
refresh();
wattroff(sdr, COLOR_PAIR(4));
wrefresh(sdr);
}
/* define rtl-sdr specific functions and general SDR workflow - WIP */
void rtl_sdr_processing(unsigned char *buf, int buffer_length) {
int n = buffer_length;
double *out = (double *)malloc(n * sizeof(double));
@ -281,7 +329,7 @@ void rtl_sdr_processing(unsigned char *buf, int buffer_length) {
void rtl_sdr_waterfall() {
bandwidth = 10*MHz;
rtlsdr_set_tuner_bandwidth(dev, bandwidth);
uint32_t buffer_length = 40, n_read = 10; // example buffer length
uint32_t buffer_length = getmaxy(sdr)-2, n_read = 10; // example buffer length
unsigned char *buffer = (unsigned char *)malloc(buffer_length);
rtlsdr_set_center_freq(dev, 104*MHz); //104MHz for testing purposes
center_freq = rtlsdr_get_center_freq(dev);
@ -290,17 +338,77 @@ void rtl_sdr_waterfall() {
usleep(500000);
}
void sdr_not_available() {
clear();
attron(COLOR_PAIR(1));
printw("SDR NOT AVAILABLE");
attroff(COLOR_PAIR(1));
refresh();
usleep(5000000);
}
/* RSS feed functions & workflow - WIP */
/* DEFCON level functions */
char fetch_defcon_level() {
CURL *curl;
CURLcode res;
FILE *fp;
unsigned int current_level = 5; //nothing ever happens
unsigned int last_known;
cooldown_timer++;
if(cooldown_timer > cooldown_reset){
cooldown_reset = true;
}
if (cooldown_reset = true) {
curl = curl_easy_init();
if (curl) {
fp = fopen(".last_known_defcon", "w");
if (fp == NULL) {
perror("fopen");
return current_level;
}
curl_easy_setopt(curl, CURLOPT_URL, DEFCON_URL);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
res = curl_easy_perform(curl);
if (res != CURLE_OK) {
fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
} else {
fseek(fp, 0, SEEK_SET);
int ch = fgetc(fp);
if (isdigit(ch)){
current_level = ch - '0'; // read the DEFCON level from the file
last_known = current_level;
}
}
fclose(fp);
curl_easy_cleanup(curl);
} else {
print_centered(defcon, "HTTP request failed :(");
}
return current_level;
}
else {
current_level = last_known;
return current_level;
}
}
void print_file_lines_ncurses(const char *filename, WINDOW *win) {
FILE *file = fopen(filename, "r");
if (file == NULL) {
perror("fopen");
return;
}
char line[256];
int y = 1;
while (fgets(line, sizeof(line), file) != NULL) {
mvwprintw(win, y++, 1, "%s", line);
wrefresh(win);
}
fclose(file);
}
/* main dashboard function */
@ -309,20 +417,90 @@ void start_dashboard() {
cbreak();
noecho();
init_colors();
if (SDR_PRESENT == true) {
do{
rtl_sdr_waterfall();
} while (true);
}
else {
do{
sdr_not_available();
} while (true);
}
rtlsdr_close(dev);
endwin();
}
refresh();
WINDOW * sdr = newwin(term_rows/2-2, term_cols/2, 1, 1);
WINDOW * rss = newwin(term_rows-2, term_cols/2-2, 1, term_cols/2+2);
WINDOW * defcon = newwin(term_rows/2, term_cols/2, term_rows/2, 1);
box(sdr, 0, 0);
box(rss, 0, 0);
box(defcon, 0, 0);
wrefresh(sdr);
wrefresh(rss);
wrefresh(defcon);
while(true) {
/* SDR window */
if (SDR_PRESENT == true) {
rtl_sdr_waterfall();
}
else {
wbkgd(sdr, COLOR_PAIR(8));
print_centered(sdr, "SDR NOT AVAILABLE");
wrefresh(sdr);
usleep(1000000);
}
/* RSS window */
if (COMMS_AVAILABLE == true && CURL_AVAILABLE == true) {
wbkgd(rss, COLOR_PAIR(9));
print_centered(rss, "RSS FEED AVAILABLE");
wrefresh(rss);
usleep(1000000);
}
else {
wbkgd(rss, COLOR_PAIR(8));
print_centered(rss, "RSS FEEED NOT AVAILABLE");
wrefresh(rss);
usleep(1000000);
}
/* DEFCON window */
if (COMMS_AVAILABLE == true && CURL_AVAILABLE == true) {
level = fetch_defcon_level();
switch(level) {
case 5:
wbkgd(defcon, COLOR_PAIR(4));
print_file_lines_ncurses("./ascii/level5", defcon);
wprintw(defcon, " DEFCON 5 - NOTHING EVER HAPPENS");
break;
case 4:
wbkgd(defcon, COLOR_PAIR(2));
print_file_lines_ncurses("./ascii/level4", defcon);
wprintw(defcon, " DEFCON 4 - UNLIKELY TO HAPPEN");
break;
case 3:
wbkgd(defcon, COLOR_PAIR(5));
print_file_lines_ncurses("./ascii/level3", defcon);
wprintw(defcon, " DEFCON 3 - MAYBE IT WILL HAPPEN");
break;
case 2:
wbkgd(defcon, COLOR_PAIR(3));
print_file_lines_ncurses("./ascii/level2", defcon);
wprintw(defcon, " DEFCON 2 - SOMETHING WILL HAPPEN");
break;
case 1:
wbkgd(defcon, COLOR_PAIR(1));
print_file_lines_ncurses("./ascii/level1", defcon);
wprintw(defcon, " DEFCON 1 - IT IS HAPPENING");
break;
default:
wbkgd(defcon, COLOR_PAIR(4));
print_file_lines_ncurses("./ascii/level5", defcon);
wprintw(defcon, " DEFCON 5 - NOTHING EVER HAPPENS"); //because nothing ever happens
break;
}
wrefresh(defcon);
usleep(1000000);
}
else {
wbkgd(defcon, COLOR_PAIR(8));
print_centered(defcon, "DEFCON CHECK NOT AVAILABLE");
wrefresh(defcon);
usleep(1000000);
}
rtlsdr_close(dev);
endwin();
}
}
int main() {
startup_screen();