Initial commit

This commit is contained in:
Joop Schilder 2021-04-29 21:58:02 +02:00
commit 58ee87a861
17 changed files with 729 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/.idea/
/vendor/

35
bin/app.php Normal file
View File

@ -0,0 +1,35 @@
<?php
// http://enjoythemusic.com/diy/0311/crossover.htm
use Scientific\Frequency;
use Scientific\Impedance;
require_once __DIR__ . '/../vendor/autoload.php';
$frequency = new Frequency(unit(9000));
$tweeter = new Driver(new Impedance(unit(8)));
$woofer = new Driver(new Impedance(unit(4)));
$crossover = new FirstOrderTwoWayCrossover($frequency);
$capacitor = $crossover->getTweeterCapacitor($tweeter);
$inductor = $crossover->getWooferInductor($woofer);
printf(
'First order 2-way crossover @ %s (slope = %s):%s',
$crossover->getFrequency(),
$crossover->getSlope(),
PHP_EOL
);
printf(
' - %s capacitor in series with %s tweeter%s',
$capacitor->getCapacitance(),
$tweeter->getImpedance(),
PHP_EOL
);
printf(
' - %s inductor in series with %s woofer%s',
$inductor->getInductance()->format('milli'),
$woofer->getImpedance(),
PHP_EOL
);

13
composer.json Normal file
View File

@ -0,0 +1,13 @@
{
"name": "joopschilder/speaker-params",
"description": "Speaker parameter modelling program",
"autoload": {
"files": ["src/Lib/helpers.php"],
"psr-0": {
"": ["src"]
}
},
"require-dev": {
"symfony/var-dumper": "^5.2"
}
}

270
composer.lock generated Normal file
View File

@ -0,0 +1,270 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "997f1773a8a4495e430433bfebab7f7c",
"packages": [],
"packages-dev": [
{
"name": "symfony/polyfill-mbstring",
"version": "v1.22.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "5232de97ee3b75b0360528dae24e73db49566ab1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/5232de97ee3b75b0360528dae24e73db49566ab1",
"reference": "5232de97ee3b75b0360528dae24e73db49566ab1",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"suggest": {
"ext-mbstring": "For best performance"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.22-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Mbstring\\": ""
},
"files": [
"bootstrap.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for the Mbstring extension",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"mbstring",
"polyfill",
"portable",
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.22.1"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2021-01-22T09:19:47+00:00"
},
{
"name": "symfony/polyfill-php80",
"version": "v1.22.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php80.git",
"reference": "dc3063ba22c2a1fd2f45ed856374d79114998f91"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/dc3063ba22c2a1fd2f45ed856374d79114998f91",
"reference": "dc3063ba22c2a1fd2f45ed856374d79114998f91",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.22-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Php80\\": ""
},
"files": [
"bootstrap.php"
],
"classmap": [
"Resources/stubs"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Ion Bazan",
"email": "ion.bazan@gmail.com"
},
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"polyfill",
"portable",
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-php80/tree/v1.22.1"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2021-01-07T16:49:33+00:00"
},
{
"name": "symfony/var-dumper",
"version": "v5.2.6",
"source": {
"type": "git",
"url": "https://github.com/symfony/var-dumper.git",
"reference": "89412a68ea2e675b4e44f260a5666729f77f668e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/89412a68ea2e675b4e44f260a5666729f77f668e",
"reference": "89412a68ea2e675b4e44f260a5666729f77f668e",
"shasum": ""
},
"require": {
"php": ">=7.2.5",
"symfony/polyfill-mbstring": "~1.0",
"symfony/polyfill-php80": "^1.15"
},
"conflict": {
"phpunit/phpunit": "<5.4.3",
"symfony/console": "<4.4"
},
"require-dev": {
"ext-iconv": "*",
"symfony/console": "^4.4|^5.0",
"symfony/process": "^4.4|^5.0",
"twig/twig": "^2.13|^3.0.4"
},
"suggest": {
"ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).",
"ext-intl": "To show region name in time zone dump",
"symfony/console": "To use the ServerDumpCommand and/or the bin/var-dump-server script"
},
"bin": [
"Resources/bin/var-dump-server"
],
"type": "library",
"autoload": {
"files": [
"Resources/functions/dump.php"
],
"psr-4": {
"Symfony\\Component\\VarDumper\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Provides mechanisms for walking through any arbitrary PHP variable",
"homepage": "https://symfony.com",
"keywords": [
"debug",
"dump"
],
"support": {
"source": "https://github.com/symfony/var-dumper/tree/v5.2.6"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2021-03-28T09:42:18+00:00"
}
],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": [],
"platform-dev": [],
"plugin-api-version": "2.0.0"
}

View File

@ -0,0 +1,19 @@
<?php
namespace Components;
use Scientific\Capacitance;
class Capacitor
{
public function __construct(
private float $capacitance
)
{
}
public function getCapacitance(): Capacitance
{
return Capacitance::fromValue($this->capacitance);
}
}

View File

@ -0,0 +1,19 @@
<?php
namespace Components;
use Scientific\Inductance;
class Inductor
{
public function __construct(
private float $inductance
)
{
}
public function getInductance(): Inductance
{
return Inductance::fromValue($this->inductance);
}
}

17
src/Driver.php Normal file
View File

@ -0,0 +1,17 @@
<?php
use Scientific\Impedance;
class Driver
{
public function __construct(
private Impedance $impedance
)
{
}
public function getImpedance(): Impedance
{
return clone $this->impedance;
}
}

View File

@ -0,0 +1,44 @@
<?php
use Components\Capacitor;
use Components\Inductor;
use Scientific\CrossoverSlope;
use Scientific\Frequency;
class FirstOrderTwoWayCrossover
{
private const PI = 3.1415926535;
private const TWO_PI = 2. * self::PI;
public function __construct(
private Frequency $frequency
)
{
}
public function getFrequency(): Frequency
{
return clone $this->frequency;
}
public function getSlope(): CrossoverSlope
{
return CrossoverSlope::fromDecibelPerOctave(6.);
}
public function getTweeterCapacitor(Driver $driver): Capacitor
{
$f = $this->getFrequency()->getValue()->unit();
$z = $driver->getImpedance()->getValue()->unit();
return new Capacitor(1. / (self::TWO_PI * $f * $z));
}
public function getWooferInductor(Driver $driver): Inductor
{
$f = $this->getFrequency()->getValue()->unit();
$z = $driver->getImpedance()->getValue()->unit();
return new Inductor($z / (self::TWO_PI * $f));
}
}

10
src/Lib/helpers.php Normal file
View File

@ -0,0 +1,10 @@
<?php
use Scientific\Unit;
if (!function_exists('unit')) {
function unit(float $value): Unit
{
return new Unit($value);
}
}

View File

@ -0,0 +1,16 @@
<?php
namespace Scientific;
class Capacitance extends Quantity
{
protected function getSymbol(): string
{
return 'F';
}
protected function getName(): string
{
return 'farad';
}
}

View File

@ -0,0 +1,26 @@
<?php
namespace Scientific;
class CrossoverSlope extends Quantity
{
public static function fromDecibelPerOctave(float $dbPerOctave): static
{
return static::fromValue($dbPerOctave / 10.);
}
protected function getSymbol(): string
{
return 'B/Oct';
}
protected function getName(): string
{
return 'slope';
}
public function __toString(): string
{
return $this->format('deci');
}
}

View File

@ -0,0 +1,16 @@
<?php
namespace Scientific;
class Frequency extends Quantity
{
protected function getSymbol(): string
{
return 'Hz';
}
protected function getName(): string
{
return 'hertz';
}
}

View File

@ -0,0 +1,7 @@
<?php
namespace Scientific;
class Impedance extends Resistance
{
}

View File

@ -0,0 +1,16 @@
<?php
namespace Scientific;
class Inductance extends Quantity
{
protected function getSymbol(): string
{
return 'H';
}
protected function getName(): string
{
return 'henry';
}
}

View File

@ -0,0 +1,86 @@
<?php
namespace Scientific;
use InvalidArgumentException;
abstract class Quantity
{
/*
* https://en.wikipedia.org/wiki/Metric_prefix
*/
private const METRIC_PREFIXES = [
'yotta' => 'Y',
'zetta' => 'Z',
'exa' => 'E',
'peta' => 'P',
'tera' => 'T',
'giga' => 'G',
'mega' => 'M',
'kilo' => 'k',
'hecto' => 'h',
'deca' => 'da',
'unit' => '',
'deci' => 'd',
'centi' => 'c',
'milli' => 'm',
'micro' => 'μ',
'nano' => 'n',
'pico' => 'p',
'femto' => 'f',
'atto' => 'a',
'zepto' => 'z',
'yocto' => 'y',
];
abstract protected function getSymbol(): string;
abstract protected function getName(): string;
public function __construct(
private Unit $value
)
{
}
final public static function fromValue(float $value): static
{
return new static(new Unit($value));
}
final public function getValue(): Unit
{
return $this->value;
}
final public function format(string $prefix = 'auto'): string
{
if ($prefix === 'auto') {
$prefix = $this->getAppropriatePrefix();
} else if (!method_exists($this->value, $prefix)) {
throw new InvalidArgumentException('Invalid size: ' . $prefix);
}
$value = $this->value->{$prefix}();
$prefix = self::METRIC_PREFIXES[$prefix] ?? '?';
$symbol = $this->getSymbol();
return sprintf('%.3g %s%s', $value, $prefix, $symbol);
}
private function getAppropriatePrefix(): string
{
foreach (array_keys(self::METRIC_PREFIXES) as $prefix) {
$value = $this->value->{$prefix}();
if ($value >= 1. && $value < 1000.) {
return $prefix;
}
}
return 'unit';
}
public function __toString(): string
{
return $this->format();
}
}

View File

@ -0,0 +1,16 @@
<?php
namespace Scientific;
class Resistance extends Quantity
{
protected function getSymbol(): string
{
return 'Ω';
}
protected function getName(): string
{
return 'ohm';
}
}

117
src/Scientific/Unit.php Normal file
View File

@ -0,0 +1,117 @@
<?php
namespace Scientific;
class Unit
{
public function __construct(
private float $value
)
{
}
public function yotta(): float
{
return $this->zetta() / 1000.;
}
public function zetta(): float
{
return $this->exa() / 1000.;
}
public function exa(): float
{
return $this->peta() / 1000.;
}
public function peta(): float
{
return $this->tera() / 1000.;
}
public function tera(): float
{
return $this->giga() / 1000.;
}
public function giga(): float
{
return $this->mega() / 1000.;
}
public function mega(): float
{
return $this->kilo() / 1000.;
}
public function kilo(): float
{
return $this->unit() / 1000.;
}
public function hecto(): float
{
return $this->unit() / 100.;
}
public function deca(): float
{
return $this->unit() / 10.;
}
public function unit(): float
{
return $this->value;
}
public function deci(): float
{
return 10. * $this->unit();
}
public function centi(): float
{
return 100. * $this->unit();
}
public function milli(): float
{
return 1000. * $this->unit();
}
public function micro(): float
{
return 1000. * $this->milli();
}
public function nano(): float
{
return 1000. * $this->micro();
}
public function pico(): float
{
return 1000. * $this->nano();
}
public function femto(): float
{
return 1000. * $this->pico();
}
public function atto(): float
{
return 1000. * $this->femto();
}
public function zepto(): float
{
return 1000. * $this->atto();
}
public function yocto(): float
{
return 1000. * $this->yocto();
}
}