lunedì 12 febbraio 2018

Opencv Android SDK su Android Studio

per utilizzare OpenCV su Android mediante l'SDK si procede prima scaricando il voluminoso pacchetto (sono oltre 300 Mb ma sono contenuti gli apk di tutte le applicazioni demo, la documentazione e le librerie compilate per differenti piattaforme hardware)

A questo punto da Android Studio (devono esserer gia' installati NDK e CMake) si crea un normale progetto ... poi /File/New/Import Module e si cerca la directory nell'SDK opencv/sdk/java

Si deve poi editare il file opencv/build.gradle

CompileSDKVersion 23
buildToolsVersion "23.0.2"
TargetSDKVersion 23

fatto cio' si mette la visualizzazione dell'albero di destra (in Project Files) si seleziona Android/App/tasto destro/Open Module Settings/Dependecies/tasto +/Module Dependencies/ e si seleziona OpenCV Library


si va poi in App/New/Folder/JNIFolder/Change Folder Location e si seleziona /src/main/jniLibs

si apre il folder e si copiano tutte le sottodirectories dall'SDK opncv/sdk/native/libs/ cancellando poi cio' che non e' *.so

Per provare se se le cose funzionavano ho provato ad usare il secondo esempio dell'SDK (quello che implementa il filtro Canny in realtime) ed il programma di esempio si ostinava a non funzionare. Dopo un po' di tempo perso a pigiare tasti ho scoperto che commentando la riga

System.loadLibrary("mixed_sample")

tutto funziona correttamente


giovedì 8 febbraio 2018

Ricerca di tweet

Un semplice esempio di come ricercare in modo automatico dei tweet mediante l'uso delle API di Twitter. E' stata impiegata la libreria Python TwitterSearch modificando leggermente il file di esempio per cerca i tweet con hashtag Frana nel dintorni di 100 Km intorno alla coordinata lat 43, lng 11

Per ottenere le chiavi e gli access token si va all'indirizzo https://apps.twitter.com/ e si crea una nuova app

--------------------------------
from TwitterSearch import *

try:
tso = TwitterSearchOrder()
tso.set_keywords(['#Frana'])
tso.set_language('it')
tso.set_geocode(43.0,11.0,100)
tso.set_include_entities(False)

ts = TwitterSearch(
consumer_key = 'xxxxx',
consumer_secret = 'xxxx',
access_token = 'xxxx',
access_token_secret = 'xxxx'
)

for tweet in ts.search_tweets_iterable(tso):
print tweet['user']['screen_name']
print tweet['text']
print tweet['coordinates']
print tweet['created_at']
print "--------------------------"
except TwitterSearchException as e:
print(e)

--------------------------------

Ho notato che ci possono essere problemi con i caratteri Unicode (per esempio se si scaricano tweet in giapponese)

Questo il risultato della query del programma precedente (ricerca dell'hashtag Frana nei dintorni di Firenze



allarme24
RT @muoversintoscan: #SP477 dell'Alpe di Casaglia, avviata la rimozione di massi e detriti precipitati per la #frana. Al più presto una cor…
None
Tue Feb 06 10:33:03 +0000 2018
--------------------------
CittaMetro_FI
RT @muoversintoscan: #SP477 dell'Alpe di Casaglia, avviata la rimozione di massi e detriti precipitati per la #frana. Al più presto una cor…
None
Tue Feb 06 10:31:11 +0000 2018
--------------------------
FlorenceTV_
RT @muoversintoscan: #SP477 dell'Alpe di Casaglia, avviata la rimozione di massi e detriti precipitati per la #frana. Al più presto una cor…
None
Tue Feb 06 10:14:09 +0000 2018
--------------------------
muoversintoscan
#SP477 dell'Alpe di Casaglia, avviata la rimozione di massi e detriti precipitati per la #frana. Al più presto una… https://t.co/nCSHFc57yw
None
Tue Feb 06 10:13:30 +0000 2018
--------------------------
LuceverdeRadio
RT @muoversintoscan: ‼️ #Londa #frana ► operai sul posto per ripristino frana sulla Sp556. Fino alle 18 aperta via di Campobello con access…
None
Mon Feb 05 16:55:55 +0000 2018
--------------------------
muoversintoscan
‼️ #Londa #frana ► operai sul posto per ripristino frana sulla Sp556. Fino alle 18 aperta via di Campobello con acc… https://t.co/fNj0eGdlCt
None
Mon Feb 05 16:43:48 +0000 2018
--------------------------
CittaMetro_FI
RT @FlorenceTV_: #Frana sulla #SP556 #Stia-#Londa, intervento @CittaMetro_FI per riaprire il tratto interessato
#viabiliTOS
https://t.co/EB…
None
Mon Feb 05 16:38:50 +0000 2018
--------------------------
FlorenceTV_
#Frana sulla #SP556 #Stia-#Londa, intervento @CittaMetro_FI per riaprire il tratto interessato
#viabiliTOS… https://t.co/rppYdf3Gpg
None
Mon Feb 05 16:33:30 +0000 2018
--------------------------
muoversintoscan
🚧 In corso intervento @CittaMetro_FI per riaprire a senso unico alt. della #SP556 #Londa-#Stia al km 3+800 interess… https://t.co/2cT6vwwDPQ
None
Mon Feb 05 16:02:25 +0000 2018
--------------------------
muoversintoscan
🚫 Sulla #SP477 dell'Alpe di Casaglia interruzione della circolazione per #frana al km 15+900 dal #2febbraio al term… https://t.co/EppWeU7bme
None
Fri Feb 02 16:31:10 +0000 2018
--------------------------
TgrRai
RT @TgrRaiToscana: Tra poco nel tg il caso della villa di #Ripafratta sepolta da una #frana: nessun lavoro, nè risarcimento. Posta una targ…
None
Tue Jan 30 12:36:26 +0000 2018
--------------------------
TgrRaiToscana
Tra poco nel tg il caso della villa di #Ripafratta sepolta da una #frana: nessun lavoro, nè risarcimento. Posta una… https://t.co/e6Ww36ctov
None
Tue Jan 30 12:10:41 +0000 2018

Windows 7 in Virtualbox

Tentando di montare una macchina virtuale Windows 7 64 bit su VirtualBox (con host Mac) sono incappato in questo errore nelle primissime fasi

Error code : 0x000000C4



Il classico in cui se non ci fossero i forum non ne sarei mai venuto a capo. La soluzione e' quella di inserire la stringa da linea di comando prima di avviare la macchina virtuale



VBoxManage setextradata "Windows 7" VBoxInternal/CPUM/CMPXCHG16B 1

Fatto cio' l'installazione procede senza problemi

mercoledì 31 gennaio 2018

Esempio mini blockchain

Uno stupido esempio di blockchain senza peer to peer e senza POW...solo per fare vedere come sono collegati i vari record e come si valida un blocco


------------------------------------------------------------------------
import sqlite3, hashlib, time

conn = sqlite3.connect("blockchain.txt")
c =conn.cursor()
c.execute("CREATE TABLE IF NOT EXISTS blocks (id integer PRIMARY KEY AUTOINCREMENT, tempo integer NOT NULL,data text NOT NULL, hash text NOT NULL, precedentehash text NOT NULL)")
c.execute("DELETE FROM blocks")

#INSERISCE IL PRIMO RECORD 
#il precedenteh puo' essere impostato come si desidera
indice = 0
precedentehash = "0"
millis = int(round(time.time() * 1000))
data = "Luca"
hash = hashlib.sha256(str(indice) + precedentehash + str(millis) + data)
hex_dig = hash.hexdigest()

query = "INSERT INTO blocks (tempo,data,hash,precedentehash) VALUES ("+str(millis)+",'"+data+"','" + hex_dig+"','" + precedentehash + "')"

print query

c.execute(query)
conn.commit()

# SECONDO RECORD

c.execute("SELECT * FROM blocks ORDER BY id DESC LIMIT 1")
row =  c.fetchone()
precedentehash = row[3]
indice = row[0]+1
millis = int(round(time.time() * 1000))
data ="Innocenti"
hash = hashlib.sha256(str(indice) + precedentehash + str(millis) + data)
hex_dig = hash.hexdigest()
query = "INSERT INTO blocks (tempo,data,hash,precedentehash) VALUES ("+str(millis)+",'"+data+"','" + hex_dig+"','" + precedentehash + "')"
print query

c.execute(query)
conn.commit()


# TERZO RECORD

c.execute("SELECT * FROM blocks ORDER BY id DESC LIMIT 1")
row =  c.fetchone()
precedentehash = row[3]
indice = row[0]+1

millis = int(round(time.time() * 1000))
data ="Firenze"
hash = hashlib.sha256(str(indice) + precedentehash + str(millis) + data)
hex_dig = hash.hexdigest()
query = "INSERT INTO blocks (tempo,data,hash,precedentehash) VALUES ("+str(millis)+",'"+data+"','" + hex_dig+"','" + precedentehash + "')"
print query

c.execute(query)
conn.commit()

#un blocco e' valido se
# l'indice e' maggiore del blocco precedente
# il precedenteh del blocco in esame e' uguale all'hash del record precedente
# l'hash del blocco e' conforme
print "---------------------------------------------"
#controlla l'integrita' del blocco 3
c.execute("SELECT * FROM blocks WHERE id=3")
c3 = c.fetchone()
c.execute("SELECT * FROM blocks WHERE id=2")
c2 = c.fetchone()

if c2[0] ==(c3[0]-1):
    print "Indice OK"
if c2[3] == c3[4]:
    print "HASH precedente Corretto"
    
precedentehash = c3[4]
millis = c3[1]
data = c3[2]
indice = c3[0]
hash = hashlib.sha256(str(indice) + precedentehash + str(millis) + data)
hex_dig = hash.hexdigest()

if c3[3] == hex_dig:
    print "Blocco corretto"

   
print "---------------------------------------------"

# mostra tutto il DB
c.execute("SELECT * FROM blocks")
rows = c.fetchall()
for r in rows:
    print(r)

conn.close()

Mandelbrot su Ninentendo 3DS homebrew

Avendo a disposizione un Nintendo 3DS con Homebrew perche' non provare a farci girare un proprio programmino? Ovviamente Mandelbrot

Per prima cosa si deve impostare il compilatore scaricabile da qui

nel pacchetto e' disponibile un cartella di esempi con vari target (tra cui 3ds)...io ho modificato il programma in 3ds_examples/graphics/bitmap/24bit-color/ in cui e' mostrato in modo semplice come usare il framebuffer per creare immagini grafiche

una volta compilato il programma, invece di copiarlo sulla SD e metterlo in esecuzione sulla consolle, ho usato l'emulatore Citra che permette l'esecuzione dei file 3dsx ovvero l'output del compilatore





-----------------------------------------------------------------
#include <3ds.h>
#include <stdio.h>
#include <string.h>

#define SCREEN_WIDTH 320
#define SCREEN_HEIGHT 240
double iterazioni = 15;

double r;

float a = -2.0;
float b = -1.2;
float im = -1.2;

float dre = 3.0/SCREEN_WIDTH;
float dim = 2.4/SCREEN_HEIGHT;
float x,y,x_new,y_new;

int k,j,i;

void setPixel(u8 *fb, u16 x, u16 y, u8 red, u8 green, u8 blue) {
    fb[3 * (240 - y + (x - 1) * 240)] = blue;
    fb[3 * (240 - y + (x - 1) * 240) + 1] = green;
    fb[3 * (240 - y + (x - 1) * 240) + 2] = red;
}


int main(int argc, char **argv)
{
gfxInitDefault();
consoleInit(GFX_TOP, NULL);
printf("3DS Mandelbrot");
printf("\x1b[21;16HPress Start to exit.");
gfxSetDoubleBuffering(GFX_BOTTOM, false);
u8* fb = gfxGetFramebuffer(GFX_BOTTOM, GFX_LEFT, NULL, NULL);

for (int i=1;i<=SCREEN_WIDTH;i++)
  {
  a=a+dre;
  b=im;
  for (int j=1;j<=SCREEN_HEIGHT;j++)
  {
  b = b+dim;
  x = 0;
  y = 0;
  for (k=0;k<=iterazioni;k++)
    {
    x_new = (x*x)-(y*y)+a;
    y_new = (2*x*y)+b;
    if (((x_new*x_new)+(y_new*y_new))>4)
    {
    r = k%2;
if (r == 0) setPixel(fb, i, j, 255, 255, 255);
    break;
    }
    x = x_new;
    y = y_new;
    }
  }

  }


// Main loop
while (aptMainLoop())
{
hidScanInput();
u32 kDown = hidKeysDown();
if (kDown & KEY_START) break; /
gfxFlushBuffers();
gfxSwapBuffers();
gspWaitForVBlank();
}
gfxExit();
return 0;
}

venerdì 12 gennaio 2018

Estimote telemetry su PC

Questo e' un metodo per leggere i dati di telemetria dei beacon Estimote da PC senza quindi appoggiarsi ad dispositivo mobile (telefono)

Si deve installare tramite Node.js il pacchetto

npm install noble

a questo punto si puo' scaricare l'esempio estimote-telemetry.js da GitHub e modificarlo alla bisgona.
si mette in esecuzione tramite

node test.js

questo metodo e' stato testato su Centos 7 con Lenovo T430

test.js
------------------------------------
// Packest from the Estimote family (Telemetry, Connectivity, etc.) are
// broadcast as Service Data (per "ยง 1.11. The Service Data - 16 bit UUID" from
// the BLE spec), with the Service UUID 'fe9a'.
var ESTIMOTE_SERVICE_UUID = 'fe9a';

// Once you obtain the "Estimote" Service Data, here's how to check if it's
// a Telemetry packet, and if so, how to parse it.
function parseEstimoteTelemetryPacket(data) { // data is a 0-indexed byte array/buffer

  // byte 0, lower 4 bits => frame type, for Telemetry it's always 2 (i.e., 0b0010)
  var frameType = data.readUInt8(0) & 0b00001111;
  var ESTIMOTE_FRAME_TYPE_TELEMETRY = 2;
  if (frameType != ESTIMOTE_FRAME_TYPE_TELEMETRY) { return; }

  // byte 0, upper 4 bits => Telemetry protocol version ("0", "1", "2", etc.)
  var protocolVersion = (data.readUInt8(0) & 0b11110000) >> 4;
  // this parser only understands version up to 2
  // (but at the time of this commit, there's no 3 or higher anyway :wink:)
  if (protocolVersion > 2) { return; }

  // bytes 1, 2, 3, 4, 5, 6, 7, 8 => first half of the identifier of the beacon
  var shortIdentifier = data.toString('hex', 1, 9);

  // byte 9, lower 2 bits => Telemetry subframe type
  // to fit all the telemetry data, we currently use two packets, "A" (i.e., "0")
  // and "B" (i.e., "1")
  var subFrameType = data.readUInt8(9) & 0b00000011;

  var ESTIMOTE_TELEMETRY_SUBFRAME_A = 0;
  var ESTIMOTE_TELEMETRY_SUBFRAME_B = 1;

  // ****************
  // * SUBFRAME "A" *
  // ****************
  if (subFrameType == ESTIMOTE_TELEMETRY_SUBFRAME_A) {

    // ***** ACCELERATION
    // byte 10 => acceleration RAW_VALUE on the X axis
    // byte 11 => acceleration RAW_VALUE on the Y axis
    // byte 12 => acceleration RAW_VALUE on the Z axis
    // RAW_VALUE is a signed (two's complement) 8-bit integer
    // RAW_VALUE * 2 / 127.0 = acceleration in "g-unit" (http://www.helmets.org/g.htm)
    var acceleration = {
      x: data.readInt8(10) * 2 / 127.0,
      y: data.readInt8(11) * 2 / 127.0,
      z: data.readInt8(12) * 2 / 127.0
    };

    // ***** MOTION STATE
    // byte 15, lower 2 bits
    // 0b00 ("0") when not moving, 0b01 ("1") when moving
    var isMoving = (data.readUInt8(15) & 0b00000011) == 1;

    // ***** MOTION STATE DURATION
    // byte 13 => "previous" motion state duration
    // byte 14 => "current" motion state duration
    // e.g., if the beacon is currently still, "current" will state how long
    // it's been still and "previous" will state how long it's previously been
    // in motion before it stopped moving
    //
    // motion state duration is composed of two parts:
    // - lower 6 bits is a NUMBER (unsigned 6-bit integer)
    // - upper 2 bits is a unit:
    //     - 0b00 ("0") => seconds
    //     - 0b01 ("1") => minutes
    //     - 0b10 ("2") => hours
    //     - 0b11 ("3") => days if NUMBER is < 32
    //                     if it's >= 32, then it's "NUMBER - 32" weeks
    var parseMotionStateDuration = function(byte) {
      var number = byte & 0b00111111;
      var unitCode = (byte & 0b11000000) >> 6;
      var unit;
      if (unitCode == 0) {
        unit = 'seconds';
      } else if (unitCode == 1) {
        unit = 'minutes';
      } else if (unitCode == 2) {
        unit = 'hours';
      } else if (unitCode == 3 && number < 32) {
        unit = 'days';
      } else {
        unit = 'weeks';
        number = number - 32;
      }
      return {number: number, unit: unit};
    }
    var motionStateDuration = {
      previous: parseMotionStateDuration(data.readUInt8(13)),
      current: parseMotionStateDuration(data.readUInt8(14))
    };

    // ***** GPIO
    // byte 15, upper 4 bits => state of GPIO pins, one bit per pin
    // 0 = state "low", 1 = state "high"
    var gpio = {
      pin0: (data.readUInt8(15) & 0b00010000) >> 4 ? 'high' : 'low',
      pin1: (data.readUInt8(15) & 0b00100000) >> 5 ? 'high' : 'low',
      pin2: (data.readUInt8(15) & 0b01000000) >> 6 ? 'high' : 'low',
      pin3: (data.readUInt8(15) & 0b10000000) >> 7 ? 'high' : 'low',
    };

    // ***** ERROR CODES
    var errors;
    if (protocolVersion == 2) {
      // in protocol version "2"
      // byte 15, bits 2 & 3
      // bit 2 => firmware error
      // bit 3 => clock error (likely, in beacons without Real-Time Clock, e.g.,
      //                      Proximity Beacons, the internal clock is out of sync)
      errors = {
        hasFirmwareError: ((data.readUInt8(15) & 0b00000100) >> 2) == 1,
        hasClockError: ((data.readUInt8(15) & 0b00001000) >> 3) == 1
      };
    } else if (protocolVersion == 1) {
      // in protocol version "1"
      // byte 16, lower 2 bits
      // bit 0 => firmware error
      // bit 1 => clock error
      errors = {
        hasFirmwareError: (data.readUInt8(16) & 0b00000001) == 1,
        hasClockError: ((data.readUInt8(16) & 0b00000010) >> 1) == 1
      };
    } else if (protocolVersion == 0) {
      // in protocol version "0", error codes are in subframe "B" instead
    }

    // ***** ATMOSPHERIC PRESSURE
    var pressure;
    if (protocolVersion == 2) {
      // added in protocol version "2"
      // bytes 16, 17, 18, 19 => atmospheric pressure RAW_VALUE
      // RAW_VALUE is an unsigned 32-bit integer, little-endian encoding,
      //   i.e., least-significant byte comes first
      //   e.g., if bytes are 16th = 0x12, 17th = 0x34, 18th = 0x56, 19th = 0x78
      //         then the value is 0x78563412
      // RAW_VALUE / 256.0 = atmospheric pressure in pascals (Pa)
      // note that unlike what you see on the weather forecast, this value is
      // not normalized to the sea level!
      pressure = data.readUInt32LE(16) / 256.0;
    }

    return {
      shortIdentifier,
      frameType: 'Estimote Telemetry', subFrameType: 'A', protocolVersion,
      acceleration, isMoving, motionStateDuration, pressure, gpio, errors
    };

  // ****************
  // * SUBFRAME "B" *
  // ****************
  } else if (subFrameType == ESTIMOTE_TELEMETRY_SUBFRAME_B) {

    // ***** MAGNETIC FIELD
    // byte 10 => normalized magnetic field RAW_VALUE on the X axis
    // byte 11 => normalized magnetic field RAW_VALUE on the Y axis
    // byte 12 => normalized magnetic field RAW_VALUE on the Z axis
    // RAW_VALUE is a signed (two's complement) 8-bit integer
    // RAW_VALUE / 128.0 = normalized value, between -1 and 1
    // the value will be 0 if the sensor hasn't been calibrated yet
    var magneticField = {
      x: data.readInt8(10) / 128.0,
      y: data.readInt8(11) / 128.0,
      z: data.readInt8(12) / 128.0
    };

    // ***** AMBIENT LIGHT
    // byte 13 => ambient light level RAW_VALUE
    // the RAW_VALUE byte is split into two halves
    // pow(2, RAW_VALUE_UPPER_HALF) * RAW_VALUE_LOWER_HALF * 0.72 = light level in lux (lx)
    var ambientLightUpper = (data.readUInt8(13) & 0b11110000) >> 4;
    var ambientLightLower = data.readUInt8(13) & 0b00001111;
    var ambientLightLevel = Math.pow(2, ambientLightUpper) * ambientLightLower * 0.72;

    // ***** BEACON UPTIME
    // byte 14 + 6 lower bits of byte 15 (i.e., 14 bits total)
    // - the lower 12 bits (i.e., byte 14 + lower 4 bits of byte 15) are
    //   a 12-bit unsigned integer
    // - the upper 2 bits (i.e., bits 4 and 5 of byte 15) denote the unit:
    //   0b00 = seconds, 0b01 = minutes, 0b10 = hours, 0b11 = days
    var uptimeUnitCode = (data.readUInt8(15) & 0b00110000) >> 4;
    var uptimeUnit;
    switch (uptimeUnitCode) {
      case 0: uptimeUnit = 'seconds'; break;
      case 1: uptimeUnit = 'minutes'; break;
      case 2: uptimeUnit = 'hours'; break;
      case 3: uptimeUnit = 'days'; break;
    }
    var uptime = {
      number: ((data.readUInt8(15) & 0b00001111) << 8) | data.readUInt8(14),
      unit: uptimeUnit
    };

    // ***** AMBIENT TEMPERATURE
    // upper 2 bits of byte 15 + byte 16 + lower 2 bits of byte 17
    // => ambient temperature RAW_VALUE, signed (two's complement) 12-bit integer
    // RAW_VALUE / 16.0 = ambient temperature in degrees Celsius
    var temperatureRawValue =
      ((data.readUInt8(17) & 0b00000011) << 10) |
       (data.readUInt8(16)               <<  2) |
      ((data.readUInt8(15) & 0b11000000) >>  6);
    if (temperatureRawValue > 2047) {
      // JavaScript way to convert an unsigned integer to a signed one (:
      temperatureRawValue = temperatureRawValue - 4096;
    }
    temperature = temperatureRawValue / 16.0;

    // ***** BATTERY VOLTAGE
    // upper 6 bits of byte 17 + byte 18 => battery voltage in mini-volts (mV)
    //                                      (unsigned 14-bit integer)
    // if all bits are set to 1, it means it hasn't been measured yet
    var batteryVoltage =
       (data.readUInt8(18)               << 6) |
      ((data.readUInt8(17) & 0b11111100) >> 2);
    if (batteryVoltage == 0b11111111111111) { batteryVoltage = undefined; }

    // ***** ERROR CODES
    // byte 19, lower 2 bits
    // see subframe A documentation of the error codes
    // starting in protocol version 1, error codes were moved to subframe A,
    // thus, you will only find them in subframe B in Telemetry protocol ver 0
    var errors;
    if (protocolVersion == 0) {
      errors = {
        hasFirmwareError: (data.readUInt8(19) & 0b00000001) == 1,
        hasClockError: ((data.readUInt8(19) & 0b00000010) >> 1) == 1
      };
    }

    // ***** BATTERY LEVEL
    // byte 19 => battery level, between 0% and 100%
    // if all bits are set to 1, it means it hasn't been measured yet
    // added in protocol version 1
    var batteryLevel;
    if (protocolVersion >= 1) {
      batteryLevel = data.readUInt8(19);
      if (batteryLevel == 0b11111111) { batteryLevel = undefined; }
    }

    return {
      shortIdentifier,
      frameType: 'Estimote Telemetry', subFrameType: 'B', protocolVersion,
      magneticField, ambientLightLevel, temperature,
      uptime, batteryVoltage, batteryLevel, errors
    };
  }
}

// example how to scan & parse Estimote Telemetry packets with noble

var noble = require('noble');

noble.on('stateChange', function(state) {
  console.log('state has changed', state);
  if (state == 'poweredOn') {
    var serviceUUIDs = [ESTIMOTE_SERVICE_UUID]; // Estimote Service
    var allowDuplicates = true;
    noble.startScanning(serviceUUIDs, allowDuplicates, function(error) {
      if (error) {
        console.log('error starting scanning', error);
      } else {
        console.log('started scanning');
      }
    });
  }
});

noble.on('discover', function(peripheral) {
  var data = peripheral.advertisement.serviceData.find(function(el) {
    return el.uuid == ESTIMOTE_SERVICE_UUID;
  }).data;

  var telemetryPacket = parseEstimoteTelemetryPacket(data);
  if (telemetryPacket) { 
if (telemetryPacket['subFrameType'] == "A"){
console.log(telemetryPacket['shortIdentifier']);
//console.log(telemetryPacket['acceleration']);
console.log("AX " + telemetryPacket['acceleration']['x']);
console.log("AY " + telemetryPacket['acceleration']['y']);
console.log("AZ " + telemetryPacket['acceleration']['z']);


}
 }

});

mercoledì 10 gennaio 2018

Rotazioni con quaternioni

I quaternioni sono oggetti matematici 4 dimensionali (aw+bx+cy+dz) costituiti da una parte scalare (detta anche reale) ed una parte vettoriale a 3 dimensioni (detta immaginaria). Il loro uso in informatica e' legata al calcolo della rotazione di oggetti in 3 dimensioni e la rappresentazione geometrica del quaternione e' dato da un vettore da (0,0,0) (b,c,d) attorno al quale si sviluppa una rotazione di valore a. (il vettore rotazione nel caso non lo sia deve essere normalizzato). Un documento di lettura abbastanza semplice si puo' trovare a questo link



i vantaggi sono:

0) i quaternioni sono ottimali per calcolare le rotazioni nello spazio. Per invece individuare la posizione di un punto dello spazio e' piu' semplice la definizione cartesiana o con gli angoli di Eulero

1) rispetto ad una rotazione con gli angoli di Eulero (dove sono necessari 9 parametri) con i quaternioni sono sufficienti 4 parametri

2) la rotazione tramite gli angoli di Eulero e' data dalla successione combinata di rotazioni attorno ai tre assi cartesiani con un ordine non preordinato (per esempio si puo' ruotare prima x che su y ma lo stesso risultato puo' essere ottenuto anche invertendo l'ordine). La rotazione tramite quaternioni e' univoca in quanto quella ottimale

3) legato al punto 2 la rotazione con i quaternioni non e' soggetta a gimbal lock

Per effettuare una rotazione si utilizza una estensione del metodo per i numeri complessi in 2D.
Si crea un quaterione (R) che descriva la rotazione e poi si applica al vettore desiderato(P) con la regola RPR-1

la moltiplicazione dei tre quaternioni segue una regola un po' particolare che si riassume come



Questa codice e' stato ripreso da questo link

---------------------------------------
        //coordinate del punto da ruotare
        float x_old = 0;
        float y_old = 1;
        float z_old = 1;
        
        //angolo di rotazione
        float angle = (float) Math.toRadians(45.0);
        
        // vettore di rotazione
        float xi = 1;
        float yi = 1;
        float zi = 1;
        
        float norma = (float) Math.sqrt((xi*xi)+(yi*yi)+(zi*zi));
        
        xi = xi/norma;
        yi = yi/norma;
        zi = zi/norma;

        double w = Math.cos(angle/2.0);
        double x = xi*Math.sin(angle/2.0);
        double y = yi*Math.sin(angle/2.0);
        double z = zi*Math.sin(angle/2.0);

        float x_new = (float) ((1 - 2*y*y -2*z*z)*x_old + (2*x*y + 2*w*z)*y_old + (2*x*z-2*w*y)*z_old);
        float y_new = (float) ((2*x*y - 2*w*z)*x_old + (1 - 2*x*x - 2*z*z)*y_old + (2*y*z + 2*w*x)*z_old);
        float z_new = (float) ((2*x*z + 2*w*y)*x_old + (2*y*z - 2*w*x)*y_old + (1 - 2*x*x - 2*y*y)*z_old);
                
        System.out.println(Double.toString(x_new));
        System.out.println(Double.toString(y_new));
        System.out.println(Double.toString(z_new));
---------------------------------------


Rotazione di un cubo di spigolo 1 orientato come gli asse e con spigolo in (0,0,0) di 45° attorno ad un vettore (0,0,0)(1,1,1). Dati rappresentati con Geogebra (nella tabella a fianco i valori dei punti ruotati)


Debugger integrato ESP32S3

Aggiornamento In realta' il Jtag USB funziona anche sui moduli cinesi Il problema risiede  nell'ID USB della porta Jtag. Nel modulo...