--- a/src/devices/wifi/propagation-loss-model-test-suite.cc Mon Jan 25 09:22:19 2010 +0100 +++ a/src/devices/wifi/propagation-loss-model-test-suite.cc Mon Jan 25 10:12:13 2010 +0000 @@ -57,7 +57,8 @@ private: virtual bool DoRun (void); - typedef struct { + typedef struct + { Vector m_position; double m_pt; // dBm double m_pr; // W @@ -68,7 +69,8 @@ }; FriisPropagationLossModelTestCase::FriisPropagationLossModelTestCase () - : TestCase ("Check to see that the ns-3 Friis propagation loss model provides correct received power"), m_testVectors () + : TestCase ("Check to see that the ns-3 Friis propagation loss model provides correct received power"), + m_testVectors () { } @@ -138,10 +140,141 @@ testVector = m_testVectors.Get (i); b->SetPosition (testVector.m_position); double resultdBm = lossModel->CalcRxPower (testVector.m_pt, a, b); - double resultW = pow (10.0, resultdBm/10.0)/1000; + double resultW = pow (10.0, resultdBm / 10.0) / 1000; NS_TEST_EXPECT_MSG_EQ_TOL (resultW, testVector.m_pr, testVector.m_tolerance, "Got unexpected rcv power"); } - + + return GetErrorStatus (); +} + +// Added for Two-Ray Ground Model - tomhewer@mac.com + +class TwoRayGroundPropagationLossModelTestCase : public TestCase +{ +public: + TwoRayGroundPropagationLossModelTestCase (); + virtual ~TwoRayGroundPropagationLossModelTestCase (); + +private: + virtual bool DoRun (void); + + typedef struct + { + Vector m_position; + double m_pt; // dBm + double m_pr; // W + double m_tolerance; + } TestVector; + + TestVectors m_testVectors; +}; + +TwoRayGroundPropagationLossModelTestCase::TwoRayGroundPropagationLossModelTestCase () + : TestCase ("Check to see that the ns-3 TwoRayGround propagation loss model provides correct received power"), + m_testVectors () +{ +} + +TwoRayGroundPropagationLossModelTestCase::~TwoRayGroundPropagationLossModelTestCase () +{ +} + +bool +TwoRayGroundPropagationLossModelTestCase::DoRun (void) +{ + // wavelength at 2.4 GHz is 0.125m + Config::SetDefault ("ns3::TwoRayGroundPropagationLossModel::Lambda", DoubleValue (0.125)); + Config::SetDefault ("ns3::TwoRayGroundPropagationLossModel::SystemLoss", DoubleValue (1.0)); + + // set antenna height to 1.5m above z coordinate + Config::SetDefault ("ns3::TwoRayGroundPropagationLossModel::HeightAboveZ", DoubleValue (1.5)); + + // Select a reference transmit power of 17.0206 dBm + // Pt = 10^(17.0206/10)/10^3 = .05035702 W + double txPowerW = 0.05035702; + double txPowerdBm = 10 * log10 (txPowerW) + 30; + + // + // As with the Friis tests above, we want to test the propagation loss + // model calculations at a few chosen distances and compare the results + // to those we can manually calculate. Let us test the ns-3 calculated + // value for agreement to be within 5e-16, as above. + // + TestVector testVector; + + // Below the Crossover distance use Friis so this test should be the same as that above + // Crossover = (4 * PI * TxAntennaHeight * RxAntennaHeight) / Lamdba + // Crossover = (4 * PI * 1.5 * 1.5) / 0.125 = 226.1946m + + testVector.m_position = Vector (100, 0, 0); + testVector.m_pt = txPowerdBm; + testVector.m_pr = 4.98265e-10; + testVector.m_tolerance = 5e-16; + m_testVectors.Add (testVector); + + // These values are above the crossover distance and therefore use the Two Ray calculation + + testVector.m_position = Vector (500, 0, 0); + testVector.m_pt = txPowerdBm; + testVector.m_pr = 4.07891862e-12; + testVector.m_tolerance = 5e-16; + m_testVectors.Add (testVector); + + testVector.m_position = Vector (1000, 0, 0); + testVector.m_pt = txPowerdBm; + testVector.m_pr = 2.5493241375e-13; + testVector.m_tolerance = 5e-16; + m_testVectors.Add (testVector); + + testVector.m_position = Vector (2000, 0, 0); + testVector.m_pt = txPowerdBm; + testVector.m_pr = 1.593327585938e-14; + testVector.m_tolerance = 5e-16; + m_testVectors.Add (testVector); + + // Repeat the tests for non-zero z coordinates + + // Pr = (0.05035702 * (1.5*1.5) * (2.5*2.5)) / (500*500*500*500) = 1.13303295e-11 + // dCross = (4 * pi * 1.5 * 2.5) / 0.125 = 376.99m + testVector.m_position = Vector (500, 0, 1); + testVector.m_pt = txPowerdBm; + testVector.m_pr = 1.13303295e-11; + testVector.m_tolerance = 5e-16; + m_testVectors.Add (testVector); + + // Pr = (0.05035702 * (1.5*1.5) * (5.5*5.5)) / (1000*1000*1000*1000) = 3.42742467375e-12 + // dCross = (4 * pi * 1.5 * 5.5) / 0.125 = 829.38m + testVector.m_position = Vector (1000, 0, 4); + testVector.m_pt = txPowerdBm; + testVector.m_pr = 3.42742467375e-12; + testVector.m_tolerance = 5e-16; + m_testVectors.Add (testVector); + + // Pr = (0.05035702 * (1.5*1.5) * (11.5*11.5)) / (2000*2000*2000*2000) = 9.36522547734e-13 + // dCross = (4 * pi * 1.5 * 11.5) / 0.125 = 1734.15m + testVector.m_position = Vector (2000, 0, 10); + testVector.m_pt = txPowerdBm; + testVector.m_pr = 9.36522547734e-13; + testVector.m_tolerance = 5e-16; + m_testVectors.Add (testVector); + + + // Now, check that the received power values are expected + + Ptr a = CreateObject (); + a->SetPosition (Vector (0,0,0)); + Ptr b = CreateObject (); + + Ptr lossModel = CreateObject (); + for (uint32_t i = 0; i < m_testVectors.GetN (); ++i) + { + testVector = m_testVectors.Get (i); + b->SetPosition (testVector.m_position); + double resultdBm = lossModel->CalcRxPower (testVector.m_pt, a, b); + double resultW = pow (10.0, resultdBm / 10.0) / 1000; + NS_TEST_EXPECT_MSG_EQ_TOL (resultW, testVector.m_pr, testVector.m_tolerance, "Got unexpected rcv power"); + } + return GetErrorStatus (); } @@ -154,7 +287,8 @@ private: virtual bool DoRun (void); - typedef struct { + typedef struct + { Vector m_position; double m_pt; // dBm double m_pr; // W @@ -165,7 +299,8 @@ }; LogDistancePropagationLossModelTestCase::LogDistancePropagationLossModelTestCase () - : TestCase ("Check to see that the ns-3 Log Distance propagation loss model provides correct received power"), m_testVectors () + : TestCase ("Check to see that the ns-3 Log Distance propagation loss model provides correct received power"), + m_testVectors () { } @@ -227,7 +362,7 @@ testVector = m_testVectors.Get (i); b->SetPosition (testVector.m_position); double resultdBm = lossModel->CalcRxPower (testVector.m_pt, a, b); - double resultW = pow (10.0, resultdBm/10.0)/1000; + double resultW = pow (10.0, resultdBm / 10.0) / 1000; NS_TEST_EXPECT_MSG_EQ_TOL (resultW, testVector.m_pr, testVector.m_tolerance, "Got unexpected rcv power"); } @@ -244,6 +379,7 @@ : TestSuite ("propagation-loss-model", UNIT) { AddTestCase (new FriisPropagationLossModelTestCase); + AddTestCase (new TwoRayGroundPropagationLossModelTestCase); AddTestCase (new LogDistancePropagationLossModelTestCase); } --- a/src/devices/wifi/propagation-loss-model.cc Mon Jan 25 09:22:19 2010 +0100 +++ a/src/devices/wifi/propagation-loss-model.cc Mon Jan 25 10:12:13 2010 +0000 @@ -17,6 +17,7 @@ * * Author: Mathieu Lacage * Contributions: Timo Bingmann + * Contributions: Tom Hewer for Two Ray Ground Model */ #include "propagation-loss-model.h" @@ -39,24 +40,26 @@ { static TypeId tid = TypeId ("ns3::PropagationLossModel") .SetParent () - ; + ; return tid; } PropagationLossModel::PropagationLossModel () : m_next (0) -{} +{ +} PropagationLossModel::~PropagationLossModel () -{} +{ +} -void +void PropagationLossModel::SetNext (Ptr next) { m_next = next; } -double +double PropagationLossModel::CalcRxPower (double txPowerDbm, Ptr a, Ptr b) const @@ -83,23 +86,25 @@ RandomVariableValue (ConstantVariable (1.0)), MakeRandomVariableAccessor (&RandomPropagationLossModel::m_variable), MakeRandomVariableChecker ()) - ; + ; return tid; } RandomPropagationLossModel::RandomPropagationLossModel () : PropagationLossModel () -{} +{ +} RandomPropagationLossModel::~RandomPropagationLossModel () -{} +{ +} -double +double RandomPropagationLossModel::DoCalcRxPower (double txPowerDbm, Ptr a, Ptr b) const { double rxc = -m_variable.GetValue (); - NS_LOG_DEBUG ("attenuation coefficent="< ()) - ; + ; return tid; } FriisPropagationLossModel::FriisPropagationLossModel () -{} -void +{ +} +void FriisPropagationLossModel::SetSystemLoss (double systemLoss) { m_systemLoss = systemLoss; } -double +double FriisPropagationLossModel::GetSystemLoss (void) const { return m_systemLoss; } -void +void FriisPropagationLossModel::SetMinDistance (double minDistance) { m_minDistance = minDistance; } -double +double FriisPropagationLossModel::GetMinDistance (void) const { return m_minDistance; } -void +void FriisPropagationLossModel::SetLambda (double frequency, double speed) { m_lambda = speed / frequency; } -void +void FriisPropagationLossModel::SetLambda (double lambda) { m_lambda = lambda; } -double +double FriisPropagationLossModel::GetLambda (void) const { return m_lambda; } -double +double FriisPropagationLossModel::DbmToW (double dbm) const { - double mw = pow(10.0,dbm/10.0); + double mw = pow (10.0,dbm / 10.0); return mw / 1000.0; } @@ -228,8 +234,177 @@ double numerator = m_lambda * m_lambda; double denominator = 16 * PI * PI * distance * distance * m_systemLoss; double pr = 10 * log10 (numerator / denominator); - NS_LOG_DEBUG ("distance="< () + .AddConstructor () + .AddAttribute ("Lambda", + "The wavelength (default is 5.15 GHz at 300 000 km/s).", + DoubleValue (300000000.0 / 5.150e9), + MakeDoubleAccessor (&TwoRayGroundPropagationLossModel::m_lambda), + MakeDoubleChecker ()) + .AddAttribute ("SystemLoss", "The system loss", + DoubleValue (1.0), + MakeDoubleAccessor (&TwoRayGroundPropagationLossModel::m_systemLoss), + MakeDoubleChecker ()) + .AddAttribute ("MinDistance", + "The distance under which the propagation model refuses to give results (m)", + DoubleValue (0.5), + MakeDoubleAccessor (&TwoRayGroundPropagationLossModel::SetMinDistance, + &TwoRayGroundPropagationLossModel::GetMinDistance), + MakeDoubleChecker ()) + .AddAttribute ("HeightAboveZ", + "The height of the antenna (m) above the node's Z coordinate", + DoubleValue (0), + MakeDoubleAccessor (&TwoRayGroundPropagationLossModel::m_heightAboveZ), + MakeDoubleChecker ()) + ; + return tid; +} + +TwoRayGroundPropagationLossModel::TwoRayGroundPropagationLossModel () +{ +} +void +TwoRayGroundPropagationLossModel::SetSystemLoss (double systemLoss) +{ + m_systemLoss = systemLoss; +} +double +TwoRayGroundPropagationLossModel::GetSystemLoss (void) const +{ + return m_systemLoss; +} +void +TwoRayGroundPropagationLossModel::SetMinDistance (double minDistance) +{ + m_minDistance = minDistance; +} +double +TwoRayGroundPropagationLossModel::GetMinDistance (void) const +{ + return m_minDistance; +} +void +TwoRayGroundPropagationLossModel::SetHeightAboveZ (double heightAboveZ) +{ + m_heightAboveZ = heightAboveZ; +} +void +TwoRayGroundPropagationLossModel::SetLambda (double frequency, double speed) +{ + m_lambda = speed / frequency; +} +void +TwoRayGroundPropagationLossModel::SetLambda (double lambda) +{ + m_lambda = lambda; +} +double +TwoRayGroundPropagationLossModel::GetLambda (void) const +{ + return m_lambda; +} + +double +TwoRayGroundPropagationLossModel::DbmToW (double dbm) const +{ + double mw = pow (10.0,dbm / 10.0); + return mw / 1000.0; +} + +double +TwoRayGroundPropagationLossModel::DbmFromW (double w) const +{ + double dbm = log10 (w * 1000.0) * 10.0; + return dbm; +} + +double +TwoRayGroundPropagationLossModel::DoCalcRxPower (double txPowerDbm, + Ptr a, + Ptr b) const +{ + /* + * Two-Ray Ground equation: + * + * where Pt, Gt and Gr are in dBm units + * L, Ht and Hr are in meter units. + * + * Pr Gt * Gr * (Ht^2 * Hr^2) + * -- = (-------------------------) + * Pt d^4 * L + * + * Gt: tx gain (unit-less) + * Gr: rx gain (unit-less) + * Pt: tx power (dBm) + * d: distance (m) + * L: system loss + * Ht: Tx antenna height (m) + * Hr: Rx antenna height (m) + * lambda: wavelength (m) + * + * As with the Friis model we ignore tx and rx gain and output values + * are in dB or dBm + * + * (Ht * Ht) * (Hr * Hr) + * rx = tx + 10 log10 (-----------------------) + * (d * d * d * d) * L + */ + double distance = a->GetDistanceFrom (b); + if (distance <= m_minDistance) + { + return txPowerDbm; + } + + // Set the height of the Tx and Rx antennae + double txAntHeight = a->GetPosition ().z + m_heightAboveZ; + double rxAntHeight = b->GetPosition ().z + m_heightAboveZ; + + // Calculate a crossover distance, under which we use Friis + /* + * + * dCross = (4 * pi * Ht * Hr) / lambda + * + */ + + double dCross = (4 * PI * txAntHeight * rxAntHeight) / GetLambda (); + double tmp = 0; + if (distance <= dCross) + { + // We use Friis + double numerator = m_lambda * m_lambda; + tmp = PI * distance; + double denominator = 16 * tmp * tmp * m_systemLoss; + double pr = 10 * log10 (numerator / denominator); + NS_LOG_DEBUG ("Receiver within crossover (" << dCross << "m) for Two_ray path; using Friis"); + NS_LOG_DEBUG ("distance=" << distance << "m, attenuation coefficient=" << pr << "dB"); + return txPowerDbm + pr; + } + else // Use Two-Ray Pathloss + { + tmp = txAntHeight * rxAntHeight; + double rayNumerator = tmp * tmp; + tmp = distance * distance; + double rayDenominator = tmp * tmp * m_systemLoss; + double rayPr = 10 * log10 (rayNumerator / rayDenominator); + NS_LOG_DEBUG ("distance=" << distance << "m, attenuation coefficient=" << rayPr << "dB"); + return txPowerDbm + rayPr; + + } } // ------------------------------------------------------------------------- // @@ -257,32 +432,33 @@ DoubleValue (46.6777), MakeDoubleAccessor (&LogDistancePropagationLossModel::m_referenceLoss), MakeDoubleChecker ()) - ; + ; return tid; - + } LogDistancePropagationLossModel::LogDistancePropagationLossModel () -{} +{ +} -void +void LogDistancePropagationLossModel::SetPathLossExponent (double n) { m_exponent = n; } -void +void LogDistancePropagationLossModel::SetReference (double referenceDistance, double referenceLoss) { m_referenceDistance = referenceDistance; m_referenceLoss = referenceLoss; } -double +double LogDistancePropagationLossModel::GetPathLossExponent (void) const { return m_exponent; } - -double + +double LogDistancePropagationLossModel::DoCalcRxPower (double txPowerDbm, Ptr a, Ptr b) const @@ -308,8 +484,8 @@ */ double pathLossDb = 10 * m_exponent * log10 (distance / m_referenceDistance); double rxc = -m_referenceLoss - pathLossDb; - NS_LOG_DEBUG ("distance="< ()) - ; + ; return tid; - + } ThreeLogDistancePropagationLossModel::ThreeLogDistancePropagationLossModel () @@ -373,7 +549,7 @@ Ptr b) const { double distance = a->GetDistanceFrom (b); - NS_ASSERT(distance >= 0); + NS_ASSERT (distance >= 0); // See doxygen comments for the formula and explanation @@ -386,20 +562,20 @@ else if (distance < m_distance1) { pathLossDb = m_referenceLoss - + 10 * m_exponent0 * log10(distance / m_distance0); + + 10 * m_exponent0 * log10 (distance / m_distance0); } else if (distance < m_distance2) { pathLossDb = m_referenceLoss - + 10 * m_exponent0 * log10(m_distance1 / m_distance0) - + 10 * m_exponent1 * log10(distance / m_distance1); + + 10 * m_exponent0 * log10 (m_distance1 / m_distance0) + + 10 * m_exponent1 * log10 (distance / m_distance1); } else { pathLossDb = m_referenceLoss - + 10 * m_exponent0 * log10(m_distance1 / m_distance0) - + 10 * m_exponent1 * log10(m_distance2 / m_distance1) - + 10 * m_exponent2 * log10(distance / m_distance2); + + 10 * m_exponent0 * log10 (m_distance1 / m_distance0) + + 10 * m_exponent1 * log10 (m_distance2 / m_distance1) + + 10 * m_exponent2 * log10 (distance / m_distance2); } NS_LOG_DEBUG ("ThreeLogDistance distance=" << distance << "m, " << @@ -443,15 +619,16 @@ DoubleValue (0.75), MakeDoubleAccessor (&NakagamiPropagationLossModel::m_m2), MakeDoubleChecker ()) - ; + ; return tid; - + } NakagamiPropagationLossModel::NakagamiPropagationLossModel () -{} +{ +} -double +double NakagamiPropagationLossModel::DoCalcRxPower (double txPowerDbm, Ptr a, Ptr b) const @@ -459,7 +636,7 @@ // select m parameter double distance = a->GetDistanceFrom (b); - NS_ASSERT(distance >= 0); + NS_ASSERT (distance >= 0); double m; if (distance < m_distance1) @@ -474,30 +651,30 @@ { m = m_m2; } - + // the current power unit is dBm, but Watt is put into the Nakagami / // Rayleigh distribution. - double powerW = pow(10, (txPowerDbm - 30) / 10); + double powerW = pow (10, (txPowerDbm - 30) / 10); double resultPowerW; // switch between Erlang- and Gamma distributions: this is only for // speed. (Gamma is equal to Erlang for any positive integer m.) - unsigned int int_m = static_cast(floor(m)); + unsigned int int_m = static_cast (floor (m)); if (int_m == m) { - resultPowerW = m_erlangRandomVariable.GetValue(int_m, powerW / m); + resultPowerW = m_erlangRandomVariable.GetValue (int_m, powerW / m); } else { - resultPowerW = m_gammaRandomVariable.GetValue(m, powerW / m); + resultPowerW = m_gammaRandomVariable.GetValue (m, powerW / m); } - double resultPowerDbm = 10 * log10(resultPowerW) + 30; + double resultPowerDbm = 10 * log10 (resultPowerW) + 30; NS_LOG_DEBUG ("Nakagami distance=" << distance << "m, " << - "power=" << powerW <<"W, " << + "power=" << powerW << "W, " << "resultPower=" << resultPowerW << "W=" << resultPowerDbm << "dBm"); return resultPowerDbm; @@ -517,26 +694,28 @@ DoubleValue (-150.0), MakeDoubleAccessor (&FixedRssLossModel::m_rss), MakeDoubleChecker ()) - ; + ; return tid; } FixedRssLossModel::FixedRssLossModel () : PropagationLossModel () -{} +{ +} FixedRssLossModel::~FixedRssLossModel () -{} +{ +} -void +void FixedRssLossModel::SetRss (double rss) { m_rss = rss; } -double +double FixedRssLossModel::DoCalcRxPower (double txPowerDbm, - Ptr a, - Ptr b) const + Ptr a, + Ptr b) const { return m_rss; } --- a/src/devices/wifi/propagation-loss-model.h Mon Jan 25 09:22:19 2010 +0100 +++ a/src/devices/wifi/propagation-loss-model.h Mon Jan 25 10:12:13 2010 +0000 @@ -18,6 +18,7 @@ * Author: Mathieu Lacage * Contributions: Timo Bingmann * Contributions: Gary Pei for fixed RSS + * Contributions: Tom Hewer for porting Two Ray Ground */ #ifndef PROPAGATION_LOSS_MODEL_H @@ -182,6 +183,95 @@ double m_minDistance; }; +/* + * + * \brief a Two-Ray Ground propagation loss model ported from NS2 + * + * Two-ray ground reflection model. + * + * \f$ Pr = \frac{Pt * Gt * Gr * (ht^2 * hr^2)}{d^4 * L} \f$ + * + * The original equation in Rappaport's book assumes L = 1. + * To be consistant with the free space equation, L is added here. + * + * Ht and Hr are set at the respective nodes z coordinate plus a model parameter + * set via SetHeightAboveZ. + * + * The two-ray model does not give a good result for short distances, due to the + * oscillation caused by constructive and destructive combination of the two + * rays. Instead the Friis free-space model is used for small distances. + * + * The crossover distance, below which Friis is used, is calculated as follows: + * + * \f$ dCross = \frac{(4 * pi * Ht * Hr)}{lambda} \f$ + */ + +class TwoRayGroundPropagationLossModel : public PropagationLossModel +{ +public: + static TypeId GetTypeId (void); + TwoRayGroundPropagationLossModel (); + /** + * \param frequency (Hz) + * \param speed (m/s) + * + * Set the main wavelength used in the TwoRayGround model + * calculation. + */ + void SetLambda (double frequency, double speed); + /** + * \param lambda (m) the wavelength + * + * Set the main wavelength used in the TwoRayGround model + * calculation. + */ + void SetLambda (double lambda); + /** + * \param systemLoss (dimension-less) + * + * Set the system loss used by the TwoRayGround propagation model. + */ + void SetSystemLoss (double systemLoss); + /** + * \param minDistance the minimum distance + * + * Below this distance, the txpower is returned + * unmodified as the rxpower. + */ + void SetMinDistance (double minDistance); + /** + * \returns the minimum distance. + */ + double GetMinDistance (void) const; + /** + * \returns the current wavelength (m) + */ + double GetLambda (void) const; + /** + * \returns the current system loss (dimention-less) + */ + double GetSystemLoss (void) const; + /** + * \Set the model antenna height above the node's Z coordinate + */ + void SetHeightAboveZ (double heightAboveZ); + +private: + TwoRayGroundPropagationLossModel (const TwoRayGroundPropagationLossModel &o); + TwoRayGroundPropagationLossModel & operator = (const TwoRayGroundPropagationLossModel &o); + virtual double DoCalcRxPower (double txPowerDbm, + Ptr a, + Ptr b) const; + double DbmToW (double dbm) const; + double DbmFromW (double w) const; + + static const double PI; + double m_lambda; + double m_systemLoss; + double m_minDistance; + double m_heightAboveZ; +}; + /** * \brief a log distance propagation model. * @@ -217,7 +307,7 @@ double GetPathLossExponent (void) const; void SetReference (double referenceDistance, double referenceLoss); - + private: LogDistancePropagationLossModel (const LogDistancePropagationLossModel &o); LogDistancePropagationLossModel & operator = (const LogDistancePropagationLossModel &o); @@ -251,9 +341,9 @@ * * \f[\displaystyle L = \begin{cases} -0 & d < d_0 \\ -L_0 + 10 \cdot n_0 \log_{10}(\frac{d}{d_0}) & d_0 \leq d < d_1 \\ -L_0 + 10 \cdot n_0 \log_{10}(\frac{d_1}{d_0}) + 10 \cdot n_1 \log_{10}(\frac{d}{d_1}) & d_1 \leq d < d_2 \\ +0 & d < d_0 \ \ +L_0 + 10 \cdot n_0 \log_{10}(\frac{d}{d_0}) & d_0 \leq d < d_1 \ \ +L_0 + 10 \cdot n_0 \log_{10}(\frac{d_1}{d_0}) + 10 \cdot n_1 \log_{10}(\frac{d}{d_1}) & d_1 \leq d < d_2 \ \ L_0 + 10 \cdot n_0 \log_{10}(\frac{d_1}{d_0}) + 10 \cdot n_1 \log_{10}(\frac{d_2}{d_1}) + 10 \cdot n_2 \log_{10}(\frac{d}{d_2})& d_2 \leq d \end{cases}\f] *