diff --git a/Esiur.Analysis/Esiur.Analysis.csproj b/Esiur.Analysis/Esiur.Analysis.csproj
new file mode 100644
index 0000000..625bd34
--- /dev/null
+++ b/Esiur.Analysis/Esiur.Analysis.csproj
@@ -0,0 +1,13 @@
+
+
+
+ netstandard2.1
+ enable
+
+
+
+
+
+
+
+
diff --git a/Esiur.Analysis/Signals/Capacity.cs b/Esiur.Analysis/Signals/Capacity.cs
new file mode 100644
index 0000000..9958a04
--- /dev/null
+++ b/Esiur.Analysis/Signals/Capacity.cs
@@ -0,0 +1,69 @@
+using Esiur.Analysis.Statistics;
+using Esiur.Analysis.Units;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Security.Cryptography.X509Certificates;
+using System.Text;
+
+namespace Esiur.Analysis.Signals
+{
+ public static class Capacity
+ {
+ public struct CSI
+ {
+ public double SNR;
+ public Probability Probability;
+
+ public CSI(double snr, double probability)
+ {
+ SNR = snr;
+ Probability = probability;
+ }
+ }
+
+ public struct OutageCapacity
+ {
+ public BitRate Capacity;
+ public double MinSNR;
+ public Probability Outage;
+ public double Bandwidth;
+
+ public OutageCapacity(Probability outage, double minSNR, double bandwidth)
+ {
+ MinSNR = minSNR;
+ Outage = outage;
+ Bandwidth = bandwidth;
+ Capacity = (1 - Outage) * bandwidth * Math.Log(1 + minSNR, 2);
+ }
+
+ public override string ToString() => $" {MinSNR.ToString("F")} <{Outage}> => {Capacity}";
+ }
+
+ public static double Compute(double bandwidth, double snr)
+ => bandwidth * Math.Log(1 + snr, 2);
+
+
+ public static double ComputeErgodic(double bandwidth, CSI[] receiverCSI)
+ {
+ return bandwidth * receiverCSI.Sum(x => Math.Log(1 + x.SNR, 2) * x.Probability);
+ }
+
+ public static OutageCapacity[] ComputeOutage(double bandwidth, CSI[] receiverCSI)
+ {
+ var sorted = receiverCSI.OrderBy(x => x.SNR);
+ var rt = sorted.Select(x => {
+ var pOut = receiverCSI.Where(csi => csi.SNR < x.SNR).Sum(x => x.Probability);
+ return new OutageCapacity()
+ {
+ Outage = pOut,
+ MinSNR = x.SNR,
+ Capacity = (1 - pOut) * bandwidth * Math.Log(1 + x.SNR, 2)
+ };
+ });
+
+ return rt.ToArray();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Esiur.Analysis/Signals/DSP.cs b/Esiur.Analysis/Signals/DSP.cs
new file mode 100644
index 0000000..ee6fa6a
--- /dev/null
+++ b/Esiur.Analysis/Signals/DSP.cs
@@ -0,0 +1,55 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Esiur.Analysis.Signals
+{
+ public static class DSP
+ {
+ public static double[] ConvolveMany(params double[][] signals)
+ {
+ var rt = signals[0];
+
+ for (var i = 1; i < signals.Length; i++)
+ rt = rt.Convolve(signals[i]);
+
+ return rt;
+ }
+
+ public static double[] Convolve(this double[] signal, double[] filter)
+ {
+ var length = signal.Length + filter.Length - 1;
+ var rt = new double[length];
+
+ //for (var i = 0; i < signal.Length; i++)
+ // for (var j = 0; j < filter.Length; j++)
+ // rt[i + j] += signal[i] * filter[j];
+
+ for (var i = 0; i < length; i++)
+ {
+ for (var j = 0; j < signal.Length && i - j >= 0 && i - j < filter.Length; j++)
+ {
+ rt[i] = signal[j] * filter[i - j];
+ }
+ }
+
+ return rt;
+ }
+
+ public static double[] CrossCorrelate(this double[] signal, double[] filter)
+ {
+ var length = signal.Length + filter.Length - 1;
+ var rt = new double[length];
+ for (var i = 0; i < length; i++)
+ {
+ for (var j = 0; j < signal.Length && j + i < filter.Length; j++)
+ {
+ rt[i] = signal[j] * filter[i + j];
+ }
+ }
+
+ return rt;
+ }
+
+ }
+}
diff --git a/Esiur.Analysis/Signals/Propagation.cs b/Esiur.Analysis/Signals/Propagation.cs
new file mode 100644
index 0000000..1ac2487
--- /dev/null
+++ b/Esiur.Analysis/Signals/Propagation.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Esiur.Analysis.Signals
+{
+ public static class Propagation
+ {
+ public static readonly double LightSpeed = 2.99792458e8;
+ public static double FindReceivedPower(double transmittedPower, double distance,
+ double transmitterGain, double receiverGain, double pathLoss)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/Esiur.Analysis/Statistics/Probability.cs b/Esiur.Analysis/Statistics/Probability.cs
new file mode 100644
index 0000000..c2ec0c7
--- /dev/null
+++ b/Esiur.Analysis/Statistics/Probability.cs
@@ -0,0 +1,31 @@
+using Esiur.Analysis.Units;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Esiur.Analysis.Statistics
+{
+ public struct Probability
+ {
+ public double Value;
+
+ public static implicit operator Probability(double v) => new Probability(v);
+ public static implicit operator double(Probability v) => v.Value;
+
+ public Probability(double value)
+ {
+ if (value > 1 || value < 0)
+ throw new Exception("0 >= Probability <= 1");
+
+ Value = value;
+ }
+
+ public double ToPercentage() => Value * 100;
+ public Probability FromPercentage(double value) => new Probability(value / 100);
+
+ public override string ToString()
+ {
+ return (Math.Round(Value * 10000) / 100) + "%";
+ }
+ }
+}
diff --git a/Esiur.Analysis/Statistics/StatisticalFunctions.cs b/Esiur.Analysis/Statistics/StatisticalFunctions.cs
new file mode 100644
index 0000000..f0c5cf8
--- /dev/null
+++ b/Esiur.Analysis/Statistics/StatisticalFunctions.cs
@@ -0,0 +1,44 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Esiur.Analysis.Statistics
+{
+ public static class Statistics
+ {
+ public static double Mean(this double[] values) => values.Sum() / values.Length;
+
+ public static double Variance(this double[] x)
+ {
+ var mean = x.Mean();
+ return x.Sum(x => Math.Pow(x - mean, 2)) / x.Length;
+ }
+
+
+ public static double StdDiv(this double[] x) => Math.Sqrt(x.Variance());
+
+ public static double Covariance(this double[] x, double[] y)
+ {
+ var n = x.Length < y.Length ? x.Length : y.Length;
+ var X = x.Mean();
+ var Y = y.Mean();
+
+ double rt = 0;
+
+ for (var i = 0; i < n; i++)
+ rt += (x[i] - X) * (y[i] - Y);
+
+ return rt / n;
+ }
+
+
+ public static double RMS(this double[] x) => Math.Sqrt(x.Sum(x => x * x) / x.Length);
+
+
+ public static double Correlation(this double[] x, double[] y)
+ {
+ return x.Covariance(y) / (x.StdDiv() * y.StdDiv());
+ }
+ }
+}
diff --git a/Esiur.Analysis/Units/BitRate.cs b/Esiur.Analysis/Units/BitRate.cs
new file mode 100644
index 0000000..49de13f
--- /dev/null
+++ b/Esiur.Analysis/Units/BitRate.cs
@@ -0,0 +1,81 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+using System.Text;
+
+namespace Esiur.Analysis.Units
+{
+ public enum BitRateKind
+ {
+ Bits,
+ Octets,
+ Bytes
+ }
+
+ public struct BitRate : IComparable
+ {
+ public double Value;
+
+
+ public BitRate(double value, BitRateKind kind = BitRateKind.Bits)
+ {
+ if (kind == BitRateKind.Bytes)
+ Value = value * 8;
+ else if (kind == BitRateKind.Octets)
+ Value = value * 7;
+ else
+ Value = value;
+ }
+
+ public static implicit operator BitRate(double d) => new BitRate(d);
+ public static implicit operator double(BitRate d) => d.Value;
+
+ //public static explicit operator PowerUnit(double d) => new PowerUnit(d, PowerUnitKind.Watt);
+ //public static explicit operator double(PowerUnit d) => d.Value;
+
+ //public static PowerUnit operator +(PowerUnit a) => a;
+ //public static PowerUnit operator -(PowerUnit a) => new PowerUnit(-a.num, a.den);
+
+ public static BitRate operator +(BitRate a, BitRate b)
+ => new BitRate(a.Value + b.Value);
+
+ public static BitRate operator -(BitRate a, BitRate b)
+ => new BitRate(a.Value - b.Value);
+
+ public static BitRate operator *(BitRate a, BitRate b)
+ => new BitRate(a.Value * b.Value);
+
+ public static BitRate operator /(BitRate a, BitRate b)
+ {
+ if (b.Value == 0)
+ {
+ throw new DivideByZeroException();
+ }
+ return new BitRate(a.Value / b.Value);
+ }
+
+
+ public override string ToString()
+ {
+ if (Value >= 1e12)
+ return (Value / 1e12).ToString("F") + " tbps";
+ else if (Value >= 1e9)
+ return (Value / 1e9).ToString("F") + " gbps";
+ else if (Value >= 1e6)
+ return (Value / 1e6).ToString("F") + " mbps";
+ else if (Value >= 1e3)
+ return (Value * 1e3).ToString("F") + " kbps";
+ else
+ return Value.ToString("F") + " bps";
+ }
+
+
+ public int CompareTo(object obj)
+ {
+ if (obj is BitRate p)
+ return Value.CompareTo(p.Value);
+ else
+ return Value.CompareTo(obj);
+ }
+ }
+}
diff --git a/Esiur.Analysis/Units/PowerUnit.cs b/Esiur.Analysis/Units/PowerUnit.cs
new file mode 100644
index 0000000..eba59d7
--- /dev/null
+++ b/Esiur.Analysis/Units/PowerUnit.cs
@@ -0,0 +1,81 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+using System.Text;
+
+namespace Esiur.Analysis.Units
+{
+ public enum PowerUnitKind
+ {
+ Watt,
+ Decibel
+ }
+
+ public struct PowerUnit : IComparable
+ {
+ public double Value;
+
+
+ public PowerUnit(double value, PowerUnitKind kind = PowerUnitKind.Watt)
+ {
+ if (kind == PowerUnitKind.Watt)
+ Value = value;
+ else if (kind == PowerUnitKind.Decibel)
+ Value = FromDb(value);
+ else
+ Value = 0;
+ }
+
+ public static implicit operator PowerUnit(double d) => new PowerUnit(d, PowerUnitKind.Watt);
+ public static implicit operator double(PowerUnit d) => d.Value;
+
+ //public static explicit operator PowerUnit(double d) => new PowerUnit(d, PowerUnitKind.Watt);
+ //public static explicit operator double(PowerUnit d) => d.Value;
+
+ //public static PowerUnit operator +(PowerUnit a) => a;
+ //public static PowerUnit operator -(PowerUnit a) => new PowerUnit(-a.num, a.den);
+
+ public static PowerUnit operator +(PowerUnit a, PowerUnit b)
+ => new PowerUnit(a.Value + b.Value);
+
+ public static PowerUnit operator -(PowerUnit a, PowerUnit b)
+ => new PowerUnit(a.Value - b.Value);
+
+ public static PowerUnit operator *(PowerUnit a, PowerUnit b)
+ => new PowerUnit(a.Value * b.Value);
+
+ public static PowerUnit operator /(PowerUnit a, PowerUnit b)
+ {
+ if (b.Value == 0)
+ {
+ throw new DivideByZeroException();
+ }
+ return new PowerUnit(a.Value / b.Value);
+ }
+
+
+ public override string ToString()
+ {
+ if (Value < 1e-6)
+ return (Value * 1e9).ToString("F") + "nW";
+ else if (Value < 1e-3)
+ return (Value * 1e6).ToString("F") + "µW";
+ else if (Value < 1)
+ return (Value * 1e3).ToString("F") + "mW";
+ else
+ return Value.ToString("F") + "W";
+ }
+
+ public double ToDb() => 10 * Math.Log(10, Value);
+ public static double FromDb(double value) => Math.Pow(10, value / 10);
+
+ public int CompareTo(object obj)
+ {
+
+ if (obj is PowerUnit p)
+ return Value.CompareTo(p.Value);
+ else
+ return Value.CompareTo(obj);
+ }
+ }
+}
diff --git a/Esiur.sln b/Esiur.sln
index 104e119..b7e0ef6 100644
--- a/Esiur.sln
+++ b/Esiur.sln
@@ -10,6 +10,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Esiur.Stores.EntityCore", "
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Test", "Test\Test.csproj", "{331F82B6-6B90-4533-9718-F7C8090D8F19}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Esiur.Analysis", "Esiur.Analysis\Esiur.Analysis.csproj", "{8D52DEEA-B7E8-4C93-9B6C-EDB9C2D25211}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -32,6 +34,10 @@ Global
{331F82B6-6B90-4533-9718-F7C8090D8F19}.Debug|Any CPU.Build.0 = Debug|Any CPU
{331F82B6-6B90-4533-9718-F7C8090D8F19}.Release|Any CPU.ActiveCfg = Release|Any CPU
{331F82B6-6B90-4533-9718-F7C8090D8F19}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8D52DEEA-B7E8-4C93-9B6C-EDB9C2D25211}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8D52DEEA-B7E8-4C93-9B6C-EDB9C2D25211}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8D52DEEA-B7E8-4C93-9B6C-EDB9C2D25211}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8D52DEEA-B7E8-4C93-9B6C-EDB9C2D25211}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/Esiur/Data/IUserType.cs b/Esiur/Data/IUserType.cs
index 6485b33..7122b40 100644
--- a/Esiur/Data/IUserType.cs
+++ b/Esiur/Data/IUserType.cs
@@ -7,5 +7,7 @@ public interface IUserType
{
object Get();
void Set(object value);
+
+ object SetAndGet(object value);
}
diff --git a/Esiur/Data/VarList.cs b/Esiur/Data/VarList.cs
new file mode 100644
index 0000000..7d6eba0
--- /dev/null
+++ b/Esiur/Data/VarList.cs
@@ -0,0 +1,103 @@
+using Esiur.Core;
+using Esiur.Resource;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+using System.Text;
+
+namespace Esiur.Data
+{
+ public class VarList : IEnumerable, ICollection, ICollection
+ {
+ string propertyName;
+ IResource resource;
+
+ List list = new List();
+
+ public VarList(IResource resource, [CallerMemberName] string propertyName = "")
+ {
+ this.resource = resource;
+ this.propertyName = propertyName;
+ }
+
+ public int Count => list.Count;
+
+ public bool IsReadOnly => false;
+
+ public bool IsSynchronized => true;
+
+
+ public object SyncRoot { get; } = new object();
+
+ public void Add(T item)
+ {
+ list.Add(item);
+
+ resource?.Instance?.Modified(propertyName);
+ }
+
+ public void Clear()
+ {
+ list.Clear();
+ resource?.Instance?.Modified(propertyName);
+ }
+
+ public bool Contains(T item)
+ {
+ return list.Contains(item);
+ }
+
+ public void CopyTo(T[] array, int arrayIndex)
+ {
+ lock (SyncRoot)
+ list.CopyTo(array, arrayIndex);
+ }
+
+ public void CopyTo(Array array, int index)
+ {
+ lock (SyncRoot)
+ (list as ICollection).CopyTo(array, index);
+ }
+
+ public IEnumerator GetEnumerator()
+ {
+ return list.GetEnumerator();
+ }
+
+ public bool Remove(T item)
+ {
+ if ( list.Remove(item))
+ {
+ resource?.Instance?.Modified(propertyName);
+ return true;
+ }
+
+ return false;
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return list.GetEnumerator();
+ }
+
+
+ public T this[int index]
+ {
+ get
+ {
+ return list[index];
+ }
+ set
+ {
+ //var oldValue = list[index];
+
+ lock (SyncRoot)
+ list[index] = value;
+
+ resource?.Instance?.Modified(propertyName);
+
+ }
+ }
+ }
+}
diff --git a/Esiur/Esiur.csproj b/Esiur/Esiur.csproj
index 9203a94..4b63b3f 100644
--- a/Esiur/Esiur.csproj
+++ b/Esiur/Esiur.csproj
@@ -6,7 +6,7 @@
Ahmed Kh. Zamil
http://www.esiur.com
true
- 2.3.6
+ 2.3.7
https://github.com/esiur/esiur-dotnet
Ahmed Kh. Zamil
diff --git a/Test/Program.cs b/Test/Program.cs
index 5d5ac93..38489bb 100644
--- a/Test/Program.cs
+++ b/Test/Program.cs
@@ -45,6 +45,8 @@ using System.Collections.Generic;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
+using Esiur.Analysis.Signals;
+using Esiur.Analysis.Units;
namespace Test
{
@@ -54,6 +56,17 @@ namespace Test
static async Task Main(string[] args)
{
+
+ var outage = Capacity.ComputeOutage(20000000, new Capacity.CSI[]
+ {
+ new Capacity.CSI(PowerUnit.FromDb(20), 0.1),
+ new Capacity.CSI(PowerUnit.FromDb(15), 0.15),
+ new Capacity.CSI(PowerUnit.FromDb(10), 0.25),
+ new Capacity.CSI(PowerUnit.FromDb(5), 0.25),
+ new Capacity.CSI(PowerUnit.FromDb(0), 0.15),
+ new Capacity.CSI(PowerUnit.FromDb(-5), 0.1),
+ });
+
// Create stores to keep objects.
var system = await Warehouse.Put("sys", new MemoryStore());
var server = await Warehouse.Put("sys/server", new DistributedServer());
diff --git a/Test/Test.csproj b/Test/Test.csproj
index 00950c4..d54d448 100644
--- a/Test/Test.csproj
+++ b/Test/Test.csproj
@@ -6,6 +6,7 @@
+