Visualizzazione post con etichetta C++. Mostra tutti i post
Visualizzazione post con etichetta C++. Mostra tutti i post

giovedì 20 novembre 2025

Keyer VBand e LearnMorsewithGoogle

Partendo dal precedente post ho modificato il programma per essere compatibile con VBand Trainer oltre che con LearnMorsewithGoogle aggiungendo anche la ripetizione se il paddle viene mantenuto premuto

il  programma puo' essere usato solo con privilegi di root (o sudo) 

il primo parametro e' il device

il secondo e' il tempo di ripetizione in millisecondi

il terzo il tipo di configurazione (0=VBand con tasti parentesi quadre, 1=Google con tasti . e / ed infine 3=VBand con tasti CTRL-LEFT e CTRL-RIGHT)  

 sudo ./autokey /dev/input/event19 300 0

 

#include <linux/uinput.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/select.h>
#include <signal.h>

volatile sig_atomic_t running = 1;

void signal_handler(int sig) {
running = 0;
}

void send_key(int ufd, int keycode) {
struct input_event ev;
memset(&ev, 0, sizeof(ev));
// Key press
ev.type = EV_KEY;
ev.code = keycode;
ev.value = 1;
write(ufd, &ev, sizeof(ev));
// Sync
ev.type = EV_SYN;
ev.code = SYN_REPORT;
ev.value = 0;
write(ufd, &ev, sizeof(ev));
// Key release
ev.type = EV_KEY;
ev.code = keycode;
ev.value = 0;
write(ufd, &ev, sizeof(ev));
// Sync
ev.type = EV_SYN;
ev.code = SYN_REPORT;
ev.value = 0;
write(ufd, &ev, sizeof(ev));
}

int main(int argc, char *argv[]) {
if (argc < 4) {
printf("Usage: %s /dev/input/eventX <delay_ms> <mode>\n", argv[0]);
printf("\nModes:\n");
printf(" 0: LEFT='[' RIGHT=']' (VBand)\n");
printf(" 1: LEFT='.' RIGHT='/' (Learn Morse Google)\n");
printf(" 2: LEFT=CTRL_LEFT RIGHT=CTRL_RIGHT (VBand2)\n");
printf("\nExample: %s /dev/input/event4 100 0\n", argv[0]);
return 1;
}
const char *mouse_dev = argv[1];
int delay_ms = atoi(argv[2]);
int mode = atoi(argv[3]);
if (delay_ms <= 0) {
printf("Error: delay must be positive\n");
return 1;
}
if (mode < 0 || mode > 2) {
printf("Error: mode must be 0, 1, or 2\n");
return 1;
}
// Define key mappings based on mode
int left_key, right_key;
const char *left_label, *right_label;
switch (mode) {
case 0:
left_key = KEY_LEFTBRACE;
right_key = KEY_RIGHTBRACE;
left_label = "'['";
right_label = "']'";
break;
case 1:
left_key = KEY_DOT;
right_key = KEY_SLASH;
left_label = "'.'";
right_label = "'/'";
break;
case 2:
left_key = KEY_LEFTCTRL;
right_key = KEY_RIGHTCTRL;
left_label = "CTRL_LEFT";
right_label = "CTRL_RIGHT";
break;
}
// Open mouse device
int mfd = open(mouse_dev, O_RDONLY | O_NONBLOCK);
if (mfd < 0) {
perror("Mouse open");
return 1;
}
// GRAB EXCLUSIVE ACCESS
if (ioctl(mfd, EVIOCGRAB, 1) < 0) {
perror("Cannot grab mouse device");
close(mfd);
return 1;
}
printf("Mouse device grabbed successfully\n");
// Open uinput
int ufd = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
if (ufd < 0) {
perror("uinput open");
ioctl(mfd, EVIOCGRAB, 0);
close(mfd);
return 1;
}
// Setup virtual keyboard - register all possible keys
ioctl(ufd, UI_SET_EVBIT, EV_KEY);
ioctl(ufd, UI_SET_KEYBIT, KEY_DOT);
ioctl(ufd, UI_SET_KEYBIT, KEY_COMMA);
ioctl(ufd, UI_SET_KEYBIT, KEY_SLASH);
ioctl(ufd, UI_SET_KEYBIT, KEY_LEFTBRACE);
ioctl(ufd, UI_SET_KEYBIT, KEY_RIGHTBRACE);
ioctl(ufd, UI_SET_KEYBIT, KEY_LEFTCTRL);
ioctl(ufd, UI_SET_KEYBIT, KEY_RIGHTCTRL);
struct uinput_setup usetup;
memset(&usetup, 0, sizeof(usetup));
snprintf(usetup.name, UINPUT_MAX_NAME_SIZE, "mouse2key-mode%d", mode);
usetup.id.bustype = BUS_USB;
usetup.id.vendor = 0x1;
usetup.id.product = 0x1;
usetup.id.version = 1;
ioctl(ufd, UI_DEV_SETUP, &usetup);
ioctl(ufd, UI_DEV_CREATE);
// Setup signal handler
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
printf("Mode %d selected - Delay: %dms\n", mode, delay_ms);
printf(" Hold LEFT button = send %s repeatedly\n", left_label);
printf(" Hold RIGHT button = send %s repeatedly\n", right_label);
printf("Press Ctrl+C to exit\n\n");
struct input_event ev;
fd_set readfds;
struct timeval timeout;
int left_pressed = 0;
int right_pressed = 0;
while (running) {
// Setup select with timeout
FD_ZERO(&readfds);
FD_SET(mfd, &readfds);
int ret;
// Only use timeout if a button is held
if (left_pressed || right_pressed) {
// Send keystroke(s) immediately
if (left_pressed) {
send_key(ufd, left_key);
//if (mode < 2) printf(".");
fflush(stdout);
}
if (right_pressed) {
send_key(ufd, right_key);
//if (mode < 2) printf(",");
fflush(stdout);
}
// NOW wait for the delay
timeout.tv_sec = delay_ms / 1000;
timeout.tv_usec = (delay_ms % 1000) * 1000;
ret = select(mfd + 1, &readfds, NULL, NULL, &timeout);
if (ret < 0) {
perror("select");
break;
}
} else {
// No button held, just wait for events (blocking)
ret = select(mfd + 1, &readfds, NULL, NULL, NULL);
if (ret < 0) {
perror("select");
break;
}
}
// Check for mouse events
if (FD_ISSET(mfd, &readfds)) {
while (read(mfd, &ev, sizeof(ev)) > 0) {
if (ev.type == EV_KEY) {
if (ev.code == BTN_LEFT) {
if (ev.value == 1) {
// Left button pressed
left_pressed = 1;
//printf("\n[LEFT pressed - sending %s]\n", left_label);
} else if (ev.value == 0) {
// Left button released
left_pressed = 0;
//printf("\n[LEFT released]\n");
}
} else if (ev.code == BTN_RIGHT) {
if (ev.value == 1) {
// Right button pressed
right_pressed = 1;
//printf("\n[RIGHT pressed - sending %s]\n", right_label);
} else if (ev.value == 0) {
// Right button released
right_pressed = 0;
//printf("\n[RIGHT released]\n");
}
}
}
}
}
}
// Cleanup
printf("\nCleaning up...\n");
ioctl(ufd, UI_DEV_DESTROY);
ioctl(mfd, EVIOCGRAB, 0);
close(ufd);
close(mfd);
return 0;
}

 

sabato 22 luglio 2023

Aruco vs Apriltag condizioni reali

Aggiornamento:

Dopo aver provato un po' di tutto per correggere i dati ho scoperto che le immagini originali non sono state riprese in modo corretto. La camera satura in alcuni condizioni di luce come si vede dai due esempi sottostanti rendendo inutile l'elaborazione





Prova comparativa per misuare distanze tramite April ed Aruco tag in condizioni reali 

Lo scopo e' quello di verificare la ripetibilita' delle misure di distanza mediante tag a condizioni di luce variabile e per questo sono state effettuate misure con i tag in posizione stazionaria

E' stata impiegata una camera di sorveglianza a fuoco fisso con acquisizione ogni 10 minuti anche di notte grazie all'illuminazione ad infrarossi

I tag sono stati di dimensione di 25 cm in formato 4x4 per Aruco e 36h11 per gli Apriltag


Per determinare la distanza sono state impiegate le librerie OpenCV su Python per agli Aruco Tags mentre la libreria Apriltags3 in C++



in generale gli Apriltag risultano meglio individuabili rispetto agli Aruco tag. Di 430 immagini totali gli Apriltags sono stati individuati al 99% ad una distanza di 6 m mentre gli Aruco tag hanno prestazioni simili ma solo sulla breve distanza (4 m)


Aruco

l'elaborazione dei tag aruco indica che l'algoritmo genera molti outliers che possono essere facilmente separati dai dati corretti












Provando a smussare i dati con una media mobile a finetra oraria la situazione non migliora e si osserva un comportamento legato all'illuminazione che si ritrovera' anche dopo con gli April Tags 



Apriltag

Dall'analisi dei grafici si vede che le condizioni di illuminazione condizionano fortemente la misura della distanza mediante Apriltag. Le misure piu' stabili sono di notte quando e' attiva l'illuminazione dei led ad infrarossi

Rispetto ad Aruco ci sono molti meno outliers ed i dati sono meno rumorosi



In queste condizioni e' stato registrato un errore di standard devitation pari a 0.96% della distanza per il tag a 6.5m e dell'1% per il tag a 10 m

Se si plottano i dati a parita' di ora del giorno si vede ancora piu' chiaramente come la presenza di ombra influenza il dato di distanza

Se si fa la differenza tra le due curve l'errore scende al 0.18%


Un sistema per rimuovere l'effetto dell'illuminazione e' di correlare i dati dei due tag (sono vicini quindi sono illuminati in modo comparabile)


Per cercare di risolvere il problema delle differenti illuminazione ho provato ad elaborare le immagini mediante l'algoritmo presentato in questo articolo (Illumination Invariant Imaging: Applications in Robust Vision-based Localisation, Mapping and Classification for Autonomous Vehicles)

In estrema sintesi l'algoritmo appiattisce una immagine RGB e cerca di annullare gli effetti di differente illuminaizone (questo algoritmo funziona solo sulle immagini diurne perche' la camera di notte acquisisce a scala di grigi)


Per le elaborazioni ho usato questo progetto su Github.  (ho dovuto fare una leggere modifica perche' le immagini della camera avevano dei valori zero che ovviamente non potevano essere usati in un logaritmo)

import cv2
import numpy as np
import os
from argparse import ArgumentParser
from multiprocessing import Pool

ROBOTCAR_ALPHA = 0.4642


def transform(im):
# Assumes image is RGB-ordered
image = cv2.imread(read_dir + "/" + im, cv2.IMREAD_UNCHANGED)
r, g, b = image[:, :, 0], image[:, :, 1], image[:, :, 2]


// per eliminare gli eventuali zeri dalla matrice
r = np.where(r==0, 0.1, r)
g = np.where(g==0, 0.1, g)
b = np.where(b==0, 0.1, b)
ii_image = 0.5 + np.log(g) - ROBOTCAR_ALPHA*np.log(b) - (1-ROBOTCAR_ALPHA)*np.log(r)

# Lastly, convert from float to uint8 space
max_ii = np.max(ii_image)
min_ii = np.min(ii_image)
uii = np.uint8((ii_image - min_ii) * 256 / (max_ii - min_ii))
ii_name = write_dir + "/" + im
cv2.imwrite(ii_name, uii)
return ii_image


def transform_loop(directory):
image_names = os.listdir(directory)

# Spawn 4 worker processes to transform in parallel
p = Pool(4)
p.map(transform, image_names)


if __name__ == '__main__':
parser = ArgumentParser(
description=
'Transform images in a directory into lighting invariant color space')
parser.add_argument('--read-dir', action="store", type=str, required=True)
parser.add_argument('--write-dir', action="store", type=str, required=True)
args = parser.parse_args()

global read_dir, write_dir
read_dir = args.read_dir
write_dir = args.write_dir
transform_loop(read_dir)



dopo l'applicazione della elaborazione l'algoritmo di riconoscimento dei tag risulta molto piu' in difficolta' nel riconoscere i taf e sono state estratte solo 71 misure di distanza del tag1 e 16 misure del tag 2









Aggiornamento:

Frugando dentro al codice della demo di Apritag3 c'e' un porzione di codice che non puo' mai essere eseguito (c'e' un IF sempre True) e la condizione Falsa e' appunto l'algoritmo di Illumination Invariant 

Basta modificare la riga 156 per esempio aggiungendo un NOT si introduce il calcolo


questi sono i grafici risultanti dopo l'algoritmo. Si e' oersa la ritmicita' dell'illuminazione ma si e' persa anche la capacita' di riconoscere i tag nelle immagini trattate (per il tag 1 circa il 50%, tag2 decisamente peggio)



L'errore percentuale delle standard deviation e' pari a 1.79% per il tag1 e 1.08% per il tag 2
La differenza risiede nel valore del parametro utilizzato nell'elaborazione delle immagini




giovedì 20 luglio 2023

TI-89 scrittura diretta in memoria video

 Oltre a poter usare un buffer per generare grafica si puo' avere anche la scrittura diretta in memoria video sulla TI-89 puntando all'indirizzo 0x4C00





#define USE_TI89
#define SAVE_SCREEN // Save/Restore LCD Contents


#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <kbd.h>

char * reale = (char *) 0x4C00;
int c;

void setpixel(int x, int y)
{
int yc = 30*y;
int xc = (int)floor(x/8);
int xd = 1<<(7-(x % 8));
reale[yc+xc]=xd;
}

void pulisci(void)
{
for (c=0;c<3840;c++)
{
reale[c]=0;
}
}

int _main(void)
{
pulisci();
for (c=0;c<80;c++){
setpixel(c,c);
}
ngetchx();
return 0;
}



martedì 18 luglio 2023

Schermo virtuale su TI-89

Una delle cose divertenti con la TI-89 e' di avere il controllo completo dell'hardware con l'accesso diretto alla memoria video


La memoria dello schermo LCD inizia a 0x4C00

Si puo' fare puntare lo schermo virtuale ad una zona di memoria e poterla trattare con i comandi grafici (ogni byte di memoria contiene l'informazione di 8 pixels) 

dopo aver creato il buffer video si puo' copiarlo sulla vera memoria video per mostrare il risultato

attenzione: nonostante la TI-89 abbia uno schermo di dimensione 160x100 il codice funziona solo con le dimensione di 240x128 ovvero le dimensioni della TI-92 (la sorella maggiore)

#define USE_TI89
#define SAVE_SCREEN // Save/Restore LCD Contents


#include <stdlib.h>
#include <stdio.h>
#include <graph.h>
#include <kbd.h>
#include <string.h>

int _main(void)
{

char virtual[LCD_SIZE];
//PortSet ((void *) 0x4C00, 239, 127);

int i;
for (i=1;i<50;i++)
{
DrawPix(i,i,A_NORMAL);
}
ngetchx();
memcpy (LCD_MEM, virtual, LCD_SIZE);
ngetchx();
PortRestore();
return 0;
}

venerdì 14 luglio 2023

TI-89 Mandelbrot

 Ho scoperto per puro caso che le calcolatrici Texas Instruments TI-89 (e parenti) hanno al proprio interno un processore della classe 68000 e che possono essere programmate, oltre che in TI-Basic, anche in C...proviamo un po'



Il compilatore C che ho usato si trova a questo link https://github.com/debrouxl/gcc4ti

esistono versioni meno aggiornate ma non sono cosi' complete

Per compilare si va in /trunk/tigcc-linux/scripts e si lancia ./Install

si impostano poi le variabili di ambiente

export TIGCC=/usr/local/share/gcc4ti/

export PATH=$PATH:$TIGCC/bin

il codice di questo post e' ripreso dal precedente post 

le differenze sono :

1) la funzione main in TIGCC si chiama _main (underscore main)

2) sono necessari gli include di stdio e kbd anche se di fatto non sono richiamati dal codice


#define USE_TI89 // produce all types of files
//#define USE_TI92PLUS
//#define USE_V200

#include <stdio.h> // standard ANSI C input/output support
#include <kbd.h>
#include <tigcclib.h>

#define FIXEDPT_WBITS 4
#define WIDTH 160
#define HEIGHT 100

#include "fixedptc.h"

void _main(void) {

ClrScr();

int j,k,i;
float test;
fixedpt Re,Im,DRe,DIm;
fixedpt X,Y,DUE;
fixedpt XN,YN,YNN;
fixedpt A,B;


Re = fixedpt_rconst(-2.00390625); //finestra reale tra -2 e +0.5
Im = fixedpt_rconst(-1.205); //finestra immaginaria tra -1.2 e 1.2
DRe = fixedpt_rconst(0.015625); //2.5/160
DIm = fixedpt_rconst(0.024); // 2.4/100
DUE = fixedpt_rconst(2.0);

A = Re;

for (j=0;j<WIDTH;j++)
{
A = fixedpt_add(A,DRe);
B = Im;
for (k=0;k<HEIGHT;k++)
{
B = fixedpt_add(B,DIm);

X = fixedpt_rconst(0.0);
Y = fixedpt_rconst(0.0);

for (i=0;i<=127;i++)
{
XN=fixedpt_sub(fixedpt_mul(X,X),fixedpt_mul(Y,Y))+A; // (x*x) - (y*y) + A
YN=fixedpt_mul(X,Y); // x*y
YNN=fixedpt_mul(DUE,YN); // 2*x*y
YN=YNN + B; // 2*x*y*+B
test = fixedpt_tofloat(fixedpt_mul(XN,XN) + fixedpt_mul(YN,YN)); //(XN*XN)+(YN*YN)
if (test > 4.0)
{
//png.plot(j,k,1.0,1.0,1.0);
if (i%2) DrawPix(j,k,A_NORMAL);
break;
}
X = XN;
Y = YN;
}
}
}
}


tigcc -O2 -o timand timand.c 

il file binario avra' una estensione .89z (nel caso si compile di per TI-92 sara' .92z)

Per il trasferimento dell'eseguibile tramite cavo USB ho usato il programma TILP funzionante su Linux impostando Direct Link



Una volta trasferito il codice sul dispositivo si puo' eseguire digitando il nome del fie per esempio se il nome del file e' timand come se fosse una funzione ...per esempio timand()
In alcuni casi la calcolatrice puo' non mostrare la linea di comando ma le icone..in questo caso si preme il tasto MODE si scrolla in basso fino all'opzione APP Desktop e se seleziona OFF

Se non si vuole usare un dispositivo fisico si puo' usare l'emulatore TIEmu. In questo caso per caricare l'eseguibile e' sufficiente premere il tasto F10 e selezionare il flie .89z







Analisi MNF su spettri di riflettanza di plastica

Devo cerca di lavorare su spettri di riflettanza di plastica e la prima domanda e': quale sono le bande significative? Sono partito dal ...