Skip to content
Snippets Groups Projects
Unit.php 7.87 KiB
Newer Older
root's avatar
root committed
<?php

namespace SmartData;

abstract class Unit
{
    // Formats
    // Bit       31                                 16                                     0
    //         +--+----------------------------------+-------------------------------------+
    // Digital |0 | type                             | lenght                              |
    //         +--+----------------------------------+-------------------------------------+
    // Bit       31   29   27     24     21     18     15     12      9      6      3      0
    //         +--+----+----+------+------+------+------+------+------+------+------+------+
    // SI      |1 |NUM |MOD |sr+4  |rad+4 |m+4   |kg+4  |s+4   |A+4   |K+4   |mol+4 |cd+4  |
    //         +--+----+----+------+------+------+------+------+------+------+------+------+
    // Bits     1   2    2     3      3      3      3      3      3      3      3      3

    // Valid values for field SI
    public const DIGITAL = 0 << 31;
    public const SI      = 1 << 31;

    // Valid values for field NUM
    public const I32 = 0 << 29;       // Value is an integral number stored in the 32 last significant bits of a 32-bit little-endian integer.
    public const I64 = 1 << 29;       // Value is an integral number stored in the 64 last significant bits of a 64-bit little-endian integer.
    public const F32 = 2 << 29;       // Value is a real number stored as an IEEE 754 binary32 little-endian floating point.
    public const D64 = 3 << 29;       // Value is a real number stored as an IEEE 754 binary64 little-endian double precision floating point.
    public const NUM = self::D64;     // AND mask to select NUM bits

    // Valid values for field MOD
    public const DIR     = 0 << 27;   // Unit is described by the product of SI base units raised to the powers recorded in the remaining fields.
    public const DIV     = 1 << 27;   // Unit is U/U, where U is described by the product SI base units raised to the powers recorded in the remaining fields.
    public const LOG     = 2 << 27;   // Unit is log_e(U), where U is described by the product of SI base units raised to the powers recorded in the remaining fields.
    public const LOG_DIV = 3 << 27;   // Unit is log_e(U/U), where U is described by the product of SI base units raised to the powers recorded in the remaining fields.
    public const MOD     = self::D64; // AND mask to select MOD bits
    // Masks to select the SI units
    public const SR      = 7 << 24;
    public const RAD     = 7 << 21;
    public const M       = 7 << 18;
    public const KG      = 7 << 15;
    public const S       = 7 << 12;
    public const A       = 7 <<  9;
    public const K       = 7 <<  6;
    public const MOL     = 7 <<  3;
    public const CD      = 7 <<  0;

    // Masks to digital fields
    public const LENGHT  = (0xFFFF);
    public const TYPE    = (self::LENGHT << 16);

    public const SIZE = 4; // bytes

    public function __construct($u){
        $this->_unit = $u;
    }

    public static function interpret($u) {
        switch ($u & (1 << 31)) {
            case self::DIGITAL:
                return new Digital_Unit($u);
                break;
            case self::SI:
                return new SI_Unit($u);
                break;
            default:
                throw new InvalidUnitException("Invalid Unit: ".$u);
        }
        return null;
    }

    public static function unpack($u)  {
        return self::interpret(unpack('V', $u)[1]);
    }

    public function is_digital() : bool {
        return (($this->_unit & (1 << 31)) == self::DIGITAL);
    }

    protected $_unit;
}

// Typical SI Quantities
final class TypicalUnit extends Unit 
{
    //                           si    | mod   | sr        | rad       |  m        |  kg       |  s        |  A        |  K        |  mol      |  cd
    const Length               = 1<<31 | 0<<27 | (4+0)<<24 | (4+0)<<21 | (4+1)<<18 | (4+0)<<15 | (4+0)<<12 | (4+0)<<9  | (4+0)<<6  | (4+0)<<3  | (4+0);
    const Mass                 = 1<<31 | 0<<27 | (4+0)<<24 | (4+0)<<21 | (4+0)<<18 | (4+1)<<15 | (4+0)<<12 | (4+0)<<9  | (4+0)<<6  | (4+0)<<3  | (4+0);
    const Time                 = 1<<31 | 0<<27 | (4+0)<<24 | (4+0)<<21 | (4+0)<<18 | (4+0)<<15 | (4+1)<<12 | (4+0)<<9  | (4+0)<<6  | (4+0)<<3  | (4+0);
    const Current              = 1<<31 | 0<<27 | (4+0)<<24 | (4+0)<<21 | (4+0)<<18 | (4+0)<<15 | (4+0)<<12 | (4+1)<<9  | (4+0)<<6  | (4+0)<<3  | (4+0);
    const Electric_Current     = Typical::Current;
    const Temperature          = 1<<31 | 0<<27 | (4+0)<<24 | (4+0)<<21 | (4+0)<<18 | (4+0)<<15 | (4+0)<<12 | (4+0)<<9  | (4+1)<<6  | (4+0)<<3  | (4+0);
    const Amount_of_Substance  = 1<<31 | 0<<27 | (4+0)<<24 | (4+0)<<21 | (4+0)<<18 | (4+0)<<15 | (4+0)<<12 | (4+0)<<9  | (4+0)<<6  | (4+1)<<3  | (4+0);
    const Luminous_Intensity   = 1<<31 | 0<<27 | (4+0)<<24 | (4+0)<<21 | (4+0)<<18 | (4+0)<<15 | (4+0)<<12 | (4+0)<<9  | (4+0)<<6  | (4+0)<<3  | (4+1);
    const Area                 = 1<<31 | 0<<27 | (4+0)<<24 | (4+0)<<21 | (4+2)<<18 | (4+0)<<15 | (4+0)<<12 | (4+0)<<9  | (4+0)<<6  | (4+0)<<3  | (4+0);
    const Volume               = 1<<31 | 0<<27 | (4+0)<<24 | (4+0)<<21 | (4+3)<<18 | (4+0)<<15 | (4+0)<<12 | (4+0)<<9  | (4+0)<<6  | (4+0)<<3  | (4+0);
    const Speed                = 1<<31 | 0<<27 | (4+0)<<24 | (4+0)<<21 | (4+1)<<18 | (4+0)<<15 | (4-1)<<12 | (4+0)<<9  | (4+0)<<6  | (4+0)<<3  | (4+0);
    const Velocity             = Typical::Speed;
    const Acceleration         = 1<<31 | 0<<27 | (4+0)<<24 | (4+0)<<21 | (4+1)<<18 | (4+0)<<15 | (4-2)<<12 | (4+0)<<9  | (4+0)<<6  | (4+0)<<3  | (4+0);
}

class Digital_Unit extends Unit 
{
    public function __get($property) {
        switch ($property) {
            case 'cod': 
                return $this->_unit;
            case 'type': 
                return (($this->_unit & self::TYPE) >> 16);
            case 'lenght':
                return ($this->_unit & self::LENGHT);
            default:
                return null;
        }
    }

    public function __toString() {
        return "{{$this->unit}, D, t={$this->type}, l={$this->lenght}}";
    }
}

class SI_Unit extends Unit 
{
    public function __get($property) {
        switch ($property) {
            case 'cod': 
                return $this->_unit;
            case 'num':
                return (($this->_unit & self::NUM) >> 29);
            case 'mod':
                return (($this->_unit & self::MOD) >> 27);
            case 'sr':  
                return (($this->_unit & self::SR)  >> 24) - 4;
            case 'rad': 
                return (($this->_unit & self::RAD) >> 21) - 4;
            case 'm':   
                return (($this->_unit & self::M)   >> 18) - 4;
            case 'kg':  
                return (($this->_unit & self::KG)  >> 15) - 4;
            case 's':   
                return (($this->_unit & self::S)   >> 12) - 4;
            case 'a':   
                return (($this->_unit & self::A)   >>  9) - 4;
            case 'k':   
                return (($this->_unit & self::K)   >>  6) - 4;
            case 'mol': 
                return (($this->_unit & self::MOL) >>  3) - 4;
            case 'cd':  
                return (($this->_unit & self::CD)  >>  0) - 4;
            default:
                return null;
        }
    }

    public function __toString() {
        $string  = "{{$this->cod}, SI, n={$this->num}, m={$this->mod}, ";
        $string .= ($this->sr  != 0) ? "sr^{$this->sr}"   : '';
        $string .= ($this->rad != 0) ? "rad^{$this->rad}" : '';
        $string .= ($this->m   != 0) ? "m^{$this->m}"     : '';
        $string .= ($this->kg  != 0) ? "kg^{$this->kg}"   : '';
        $string .= ($this->s   != 0) ? "s^{$this->s}"     : '';
        $string .= ($this->a   != 0) ? "A^{$this->a}"     : '';
        $string .= ($this->k   != 0) ? "K^{$this->k}"     : '';
        $string .= ($this->mol != 0) ? "mol^{$this->mol}" : '';
        $string .= ($this->cd  != 0) ? "cd^{$this->cd}"  : '';
        $string .= '}';
        return $string;
    }

}