From 248936c84bfc9d6c3c2b5fe13ffe0095367e824f Mon Sep 17 00:00:00 2001 From: Esko Dijk Date: Wed, 27 Sep 2023 23:53:38 +0200 Subject: [PATCH] Feature: 'radioparam' command to change radiomodel parameters (#73) * [cli][radiomodel] Adding CLI command for changing radiomodel parameters at runtime. * [pylibs] Added unit test for 'radioparams'. * [cli] 'radioparam' command added to README. --- cli/CmdRunner.go | 65 ++++++++++++++++++++++ cli/README.md | 32 +++++++++++ cli/ast.go | 9 +++ cli/cli_test.go | 8 +++ pylibs/otns/cli/OTNS.py | 38 ++++++++++++- pylibs/unittests/test_radiomodels.py | 31 +++++++++-- radiomodel/pathloss_model.go | 51 ++++++++--------- radiomodel/radiomodel.go | 64 +++++++++++++++------ radiomodel/radiomodelIdeal.go | 27 ++++++--- radiomodel/radiomodelMutualInterference.go | 47 +++++++++------- 10 files changed, 293 insertions(+), 79 deletions(-) diff --git a/cli/CmdRunner.go b/cli/CmdRunner.go index 9e001628..4f83c58c 100644 --- a/cli/CmdRunner.go +++ b/cli/CmdRunner.go @@ -31,6 +31,7 @@ import ( "fmt" "io" "reflect" + "strconv" "strings" "time" @@ -252,6 +253,8 @@ func (rt *CmdRunner) execute(cmd *Command, output io.Writer) { rt.executeNetInfo(cc, cc.NetInfo) } else if cmd.RadioModel != nil { rt.executeRadioModel(cc, cc.RadioModel) + } else if cmd.RadioParam != nil { + rt.executeRadioParam(cc, cc.RadioParam) } else if cmd.Energy != nil { rt.executeEnergy(cc, cc.Energy) } else if cmd.LogLevel != nil { @@ -728,6 +731,68 @@ func (rt *CmdRunner) executeRadioModel(cc *CommandContext, cmd *RadioModelCmd) { } } +func displayRadioParam(val *reflect.Value) (string, bool) { + if val.CanFloat() { + f := val.Float() + if f == radiomodel.UndefinedDbValue { + return "undefined", false + } + return strconv.FormatFloat(f, 'f', -1, 64), true + } else if val.Bool() { + return "1", true + } + return "0", true +} + +func (rt *CmdRunner) executeRadioParam(cc *CommandContext, cmd *RadioParamCmd) { + rt.postAsyncWait(cc, func(sim *simulation.Simulation) { + rp := sim.Dispatcher().GetRadioModel().GetParameters() + rpVal := reflect.ValueOf(rp).Elem() + rpTyp := reflect.TypeOf(rp).Elem() + + // variant: radioparam + if len(cmd.Param) == 0 { + for i := 0; i < rpVal.NumField(); i++ { + fname := rpTyp.Field(i).Name + fval := rpVal.Field(i) + s, isDefined := displayRadioParam(&fval) + if isDefined { + cc.outputf("%-20s %s\n", fname, s) + } + } + return + } + + // variant: radioparam + // variant: radioparam + var fval reflect.Value + _, ok := rpTyp.FieldByName(cmd.Param) + if !ok { + cc.errorf("Unknown radiomodel parameter: %s", cmd.Param) + return + } + + fval = rpVal.FieldByName(cmd.Param) + isFloat := fval.CanFloat() + if cmd.Val == nil { // show value of single parameter + s, _ := displayRadioParam(&fval) + cc.outputf("%s\n", s) + return + } + + // set new parameter value + newVal := *cmd.Val + if cmd.Sign == "-" { + newVal = -newVal + } + if !isFloat { // if we're setting a bool parameter, use <=0 -> false ; >0 -> true + fval.SetBool(newVal > 0) + } else { + fval.SetFloat(newVal) + } + }) +} + func (rt *CmdRunner) executeLogLevel(cc *CommandContext, cmd *LogLevelCmd) { if cmd.Level == "" { cc.outputf("%v\n", logger.GetLevelString(rt.sim.GetLogLevel())) diff --git a/cli/README.md b/cli/README.md index 6c0047c8..3cfd2799 100644 --- a/cli/README.md +++ b/cli/README.md @@ -30,6 +30,7 @@ Python libraries use the CLI to manage simulations. * [plr](#plr) * [radio](#radio-node-id-node-id--on--off--ft-fail-duration-fail-interval) * [radiomodel](#radiomodel-modelname) +* [radioparam](#radioparam-param-name-new-value) * [scan](#scan-node-id) * [speed](#speed) * [title](#title-string) @@ -530,6 +531,37 @@ Done > ``` +### radioparam \[param-name\] \[new-value\] + +Get or set parameters of the current radiomodel. Use without the optional arguments to get a list of all current +radiomodel parameters. Add the `param-name` to get only the value of that parameter. If both `param-name` and `new-value` +are provided, the parameter value is set to `new-value`. It has to be a numeric value. + +```bash +> radioparam +MeterPerUnit 0.1 +IsDiscLimit 0 +RssiMinDbm -126 +RssiMaxDbm 126 +ExponentDb 17.3 +FixedLossDb 40 +NlosExponentDb 38.3 +NlosFixedLossDb 26.77 +NoiseFloorDbm -95 +SnrMinThresholdDb -4 +ShadowFadingSigmaDb 8.03 +Done +> radioparam MeterPerUnit +0.1 +Done +> radioparam MeterPerUnit 0.5 +Done +> radioparam MeterPerUnit +0.5 +Done +> +``` + ### scan \ Perform a network scan by the indicated node. diff --git a/cli/ast.go b/cli/ast.go index c1ce7b71..5acf3481 100644 --- a/cli/ast.go +++ b/cli/ast.go @@ -61,6 +61,7 @@ type Command struct { Plr *PlrCmd `| @@` //nolint Radio *RadioCmd `| @@` //nolint RadioModel *RadioModelCmd `| @@` //nolint + RadioParam *RadioParamCmd `| @@` //nolint Scan *ScanCmd `| @@` //nolint Speed *SpeedCmd `| @@` //nolint Time *TimeCmd `| @@` //nolint @@ -459,6 +460,14 @@ type RadioModelCmd struct { Model string `[(@Ident|@Int)]` //nolint } +// noinspection GoVetStructTag +type RadioParamCmd struct { + Cmd struct{} `"radioparam"` //nolint + Param string `[@Ident]` //nolint + Sign string `[@("-"|"+")]` //nolint + Val *float64 `[ (@Int|@Float) ]` //nolint +} + // noinspection GoVetStructTag type LogLevelCmd struct { Cmd struct{} `"log"` //nolint diff --git a/cli/cli_test.go b/cli/cli_test.go index 6d8776ea..d8ce5086 100644 --- a/cli/cli_test.go +++ b/cli/cli_test.go @@ -133,12 +133,20 @@ func TestParseBytes(t *testing.T) { assert.True(t, parseBytes([]byte("plr"), &cmd) == nil && cmd.Plr != nil && cmd.Plr.Val == nil) assert.True(t, parseBytes([]byte("plr 1"), &cmd) == nil && cmd.Plr != nil && *cmd.Plr.Val == 1) + assert.True(t, parseBytes([]byte("plr 0.78910"), &cmd) == nil && cmd.Plr != nil && *cmd.Plr.Val == 0.78910) assert.True(t, parseBytes([]byte("radio 1 on"), &cmd) == nil && cmd.Radio != nil) assert.True(t, parseBytes([]byte("radio 1 off"), &cmd) == nil && cmd.Radio != nil) assert.True(t, parseBytes([]byte("radio 1 2 3 on"), &cmd) == nil && cmd.Radio != nil) assert.True(t, parseBytes([]byte("radio 4 5 6 off"), &cmd) == nil && cmd.Radio != nil) assert.True(t, parseBytes([]byte("radio 4 5 6 ft 10 60"), &cmd) == nil && cmd.Radio != nil) + assert.True(t, parseBytes([]byte("radiomodel AnyName"), &cmd) == nil && cmd.RadioModel != nil && cmd.RadioModel.Model == "AnyName") + assert.True(t, parseBytes([]byte("radiomodel 42"), &cmd) == nil && cmd.RadioModel != nil && cmd.RadioModel.Model == "42") + assert.True(t, parseBytes([]byte("radiomodel"), &cmd) == nil && cmd.RadioModel != nil && cmd.RadioModel.Model == "") + assert.True(t, parseBytes([]byte("radioparam"), &cmd) == nil && cmd.RadioParam != nil && cmd.RadioParam.Param == "" && cmd.RadioParam.Val == nil) + assert.True(t, parseBytes([]byte("radioparam name1"), &cmd) == nil && cmd.RadioParam != nil && cmd.RadioParam.Param == "name1" && cmd.RadioParam.Val == nil) + assert.True(t, parseBytes([]byte("radioparam name1 0.43"), &cmd) == nil && cmd.RadioParam != nil && cmd.RadioParam.Param == "name1" && *cmd.RadioParam.Val == 0.43) + assert.True(t, parseBytes([]byte("radioparam name2 -23.512"), &cmd) == nil && cmd.RadioParam != nil && cmd.RadioParam.Param == "name2" && cmd.RadioParam.Sign == "-" && *cmd.RadioParam.Val == 23.512) assert.True(t, parseBytes([]byte("scan 1"), &cmd) == nil && cmd.Scan != nil) diff --git a/pylibs/otns/cli/OTNS.py b/pylibs/otns/cli/OTNS.py index e6dd4a44..4cc6ab9a 100644 --- a/pylibs/otns/cli/OTNS.py +++ b/pylibs/otns/cli/OTNS.py @@ -161,6 +161,8 @@ def speed(self, speed: float) -> None: @property def radiomodel(self) -> str: """ + Get radiomodel used for simulation. + :return: current radio model used """ return self._expect_str(self._do_command(f'radiomodel')) @@ -168,12 +170,46 @@ def radiomodel(self) -> str: @radiomodel.setter def radiomodel(self, model: str) -> None: """ - Set radiomodel for simulation. + Set radiomodel to be used for simulation. Setting a radiomodel also resets all the + parameters. :param model: name of new radio model to use. Default is "MutualInterference". """ assert self._do_command(f'radiomodel {model}')[0] == model + def radioparams(self) -> Dict[int, Collection[int]]: + """ + Get radiomodel parameters. + + :return: dict with parameter strings as keys and parameter values as values + """ + output = self._do_command('radioparam') + params = {} + for line in output: + line = line.split() + parname = line[0] + parvalue = float(line[1]) + params[parname] = parvalue + return params + + def get_radioparam(self, parname: str): + """ + Get a radiomodel parameter. + + :param parname: parameter name (string) + :return parameter value + """ + return float(self._do_command(f'radioparam {parname}')[0]) + + def set_radioparam(self, parname: str, parvalue: float): + """ + Set a radiomodel parameter to the specified value. + + :param parname: parameter name (string) + :param parvalue: parameter value (float) + """ + self._do_command(f'radioparam {parname} {parvalue}') + @property def loglevel(self) -> str: """ diff --git a/pylibs/unittests/test_radiomodels.py b/pylibs/unittests/test_radiomodels.py index 7b12f827..03807950 100644 --- a/pylibs/unittests/test_radiomodels.py +++ b/pylibs/unittests/test_radiomodels.py @@ -25,6 +25,7 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # + import unittest from OTNSTestCase import OTNSTestCase @@ -32,16 +33,11 @@ from test_commissioning import CommissioningTests from test_ping import PingTests from test_csl import CslTests -from otns.cli import errors, OTNS +from otns.cli import errors class RadioModelTests(OTNSTestCase): - # override - def setUp(self): - super().setUp() - self.ns.radiomodel = 'MutualInterference' - def testRadioModelSwitching(self): ns = self.ns ns.radiomodel = 'Ideal' @@ -92,6 +88,29 @@ def testRadioModelSwitching(self): ns.go(200) self.assertFormPartitions(1) + def testRadioParametersGet(self): + ns = self.ns + rp = ns.radioparams() + self.assertEqual(0,rp['IsDiscLimit']) + self.assertEqual(-126,rp['RssiMinDbm']) + self.assertEqual(126,rp['RssiMaxDbm']) + self.assertEqual(rp['NoiseFloorDbm'], ns.get_radioparam('NoiseFloorDbm')) + self.assertEqual(rp['ExponentDb'], ns.get_radioparam('ExponentDb')) + with self.assertRaises(errors.OTNSCliError): + ns.get_radioparam('NonExistingParam') + + def testRadioParametersSet(self): + ns = self.ns + ns.set_radioparam('NlosExponentDb', 48.0) + rp = ns.radioparams() + self.assertEqual(48.0, rp['NlosExponentDb']) + self.assertEqual(0, rp['IsDiscLimit']) + + ns.radiomodel = 'MIDisc' # selecting a model resets the parameters + rp = ns.radioparams() + self.assertNotEquals(48.0, rp['NlosExponentDb']) + self.assertEqual(1, rp['IsDiscLimit']) + class BasicTests_Ideal(BasicTests): diff --git a/radiomodel/pathloss_model.go b/radiomodel/pathloss_model.go index 4573d85d..063b68e5 100644 --- a/radiomodel/pathloss_model.go +++ b/radiomodel/pathloss_model.go @@ -28,38 +28,36 @@ package radiomodel import "math" -func newIndoorModelParamsItu() *RadioModelParams { - return &RadioModelParams{ - MeterPerUnit: defaultMeterPerUnit, - ExponentDb: 30.0, - FixedLossDb: 20.0*math.Log10(2400) - 28.0, - } +// custom parameter rounding function +func paround(param float64) float64 { + return math.Round(param*100.0) / 100.0 +} + +// ITU-T model +func setIndoorModelParamsItu(params *RadioModelParams) { + params.ExponentDb = 30.0 + params.FixedLossDb = paround(20.0*math.Log10(2400) - 28.0) } // see 3GPP TR 38.901 V17.0.0, Table 7.4.1-1: Pathloss models. -func newIndoorModelParams3gpp() *RadioModelParams { - return &RadioModelParams{ - MeterPerUnit: defaultMeterPerUnit, - ExponentDb: 17.3, - FixedLossDb: 32.4 + 20*math.Log10(2.4), - NlosExponentDb: 38.3, - NlosFixedLossDb: 17.3 + 24.9*math.Log10(2.4), - NoiseFloorDbm: noiseFloorIndoorDbm, - SnrMinThresholdDb: -4.0, // see calcber.m Octave file - ShadowFadingSigmaDb: 8.03, - } +func setIndoorModelParams3gpp(params *RadioModelParams) { + params.ExponentDb = 17.3 + params.FixedLossDb = paround(32.4 + 20*math.Log10(2.4)) + params.NlosExponentDb = 38.3 + params.NlosFixedLossDb = paround(17.3 + 24.9*math.Log10(2.4)) + params.NoiseFloorDbm = noiseFloorIndoorDbm + params.SnrMinThresholdDb = -4.0 // see calcber.m Octave file + params.ShadowFadingSigmaDb = 8.03 } // experimental outdoor model with LoS -func newOutdoorModelParams() *RadioModelParams { - return &RadioModelParams{ - MeterPerUnit: 0.5, - ExponentDb: 17.3, - FixedLossDb: 32.4 + 20*math.Log10(2.4), - NoiseFloorDbm: noiseFloorIndoorDbm, - SnrMinThresholdDb: -4.0, // see calcber.m Octave file - ShadowFadingSigmaDb: 3.0, - } +func setOutdoorModelParams(params *RadioModelParams) { + params.MeterPerUnit = 0.5 + params.ExponentDb = 17.3 + params.FixedLossDb = paround(32.4 + 20*math.Log10(2.4)) + params.NoiseFloorDbm = noiseFloorIndoorDbm + params.SnrMinThresholdDb = -4.0 // see calcber.m Octave file + params.ShadowFadingSigmaDb = 3.0 } // computeIndoorRssi computes the RSSI for a receiver at distance dist, using a simple indoor exponent loss model. @@ -92,7 +90,6 @@ func computeIndoorRssi3gpp(dist float64, txPower DbValue, modelParams *RadioMode pathloss = math.Max(pathloss, pathlossNLOS) } } - rssi := txPower - pathloss return rssi } diff --git a/radiomodel/radiomodel.go b/radiomodel/radiomodel.go index be95bc42..c18d72c8 100644 --- a/radiomodel/radiomodel.go +++ b/radiomodel/radiomodel.go @@ -35,6 +35,8 @@ import ( type DbValue = float64 +const UndefinedDbValue = math.MaxFloat64 + // IEEE 802.15.4-2015 related parameters for 2.4 GHz O-QPSK PHY const ( MinChannelNumber ChannelId = 0 // below 11 are sub-Ghz channels for 802.15.4-2015 @@ -97,6 +99,9 @@ type RadioModel interface { // GetName gets the display name of this RadioModel. GetName() string + // GetParameters gets the parameters of this RadioModel. These may be modified during operation. + GetParameters() *RadioModelParams + // init initializes the RadioModel. init() } @@ -104,6 +109,9 @@ type RadioModel interface { // RadioModelParams stores model parameters for the radio model. type RadioModelParams struct { MeterPerUnit float64 // the distance in meters, equivalent to a single distance unit(pixel) + IsDiscLimit bool // If true, RF signal Tx range is limited to the RadioRange set for each node + RssiMinDbm DbValue // Lowest RSSI value (dBm) that can be returned, overriding other calculations + RssiMaxDbm DbValue // Highest RSSI value (dBm) that can be returned, overriding other calculations ExponentDb DbValue // the exponent (dB) in the regular/LOS model FixedLossDb DbValue // the fixed loss (dB) term in the regular/LOS model NlosExponentDb DbValue // the exponent (dB) in the NLOS model @@ -113,43 +121,65 @@ type RadioModelParams struct { ShadowFadingSigmaDb DbValue // sigma (stddev) parameter for Shadow Fading (SF), in dB } +// newRadioModelParams gets a new set of parameters with default values, as a basis to configure further. +func newRadioModelParams() *RadioModelParams { + return &RadioModelParams{ + MeterPerUnit: defaultMeterPerUnit, + IsDiscLimit: false, + RssiMinDbm: RssiMin, + RssiMaxDbm: RssiMax, + ExponentDb: UndefinedDbValue, + FixedLossDb: UndefinedDbValue, + NlosExponentDb: UndefinedDbValue, + NlosFixedLossDb: UndefinedDbValue, + NoiseFloorDbm: UndefinedDbValue, + SnrMinThresholdDb: UndefinedDbValue, + ShadowFadingSigmaDb: UndefinedDbValue, + } +} + // NewRadioModel creates a new RadioModel with given name, or nil if model not found. func NewRadioModel(modelName string) RadioModel { var model RadioModel switch modelName { case "Ideal", "I", "1": - model = &RadioModelIdeal{ - Name: "Ideal", - FixedRssi: -60, - Params: &RadioModelParams{ - MeterPerUnit: defaultMeterPerUnit, - }, - } + model = &RadioModelIdeal{name: "Ideal", params: newRadioModelParams()} + p := model.GetParameters() + p.IsDiscLimit = true + p.RssiMinDbm = -60.0 + p.RssiMaxDbm = -60.0 + case "Ideal_Rssi", "IR", "2", "default": model = &RadioModelIdeal{ - Name: "Ideal_Rssi", - UseVariableRssi: true, - Params: newIndoorModelParamsItu(), + name: "Ideal_Rssi", + params: newRadioModelParams(), } + p := model.GetParameters() + setIndoorModelParamsItu(p) + p.IsDiscLimit = true case "MutualInterference", "MI", "M", "3": model = &RadioModelMutualInterference{ - Name: "MutualInterference", - Params: newIndoorModelParams3gpp(), + name: "MutualInterference", + params: newRadioModelParams(), shadowFading: newShadowFading(), } + setIndoorModelParams3gpp(model.GetParameters()) case "MIDisc", "MID", "4": model = &RadioModelMutualInterference{ - Name: "MIDisc", - IsDiscLimit: true, - Params: newIndoorModelParams3gpp(), + name: "MIDisc", + params: newRadioModelParams(), shadowFading: newShadowFading(), } + p := model.GetParameters() + setIndoorModelParams3gpp(p) + p.IsDiscLimit = true case "Outdoor", "5": model = &RadioModelMutualInterference{ - Name: "Outdoor", - Params: newOutdoorModelParams(), + name: "Outdoor", + params: newRadioModelParams(), shadowFading: newShadowFading(), } + setOutdoorModelParams(model.GetParameters()) default: model = nil } diff --git a/radiomodel/radiomodelIdeal.go b/radiomodel/radiomodelIdeal.go index ee480343..0499645e 100644 --- a/radiomodel/radiomodelIdeal.go +++ b/radiomodel/radiomodelIdeal.go @@ -1,4 +1,4 @@ -// Copyright (c) 2022, The OTNS Authors. +// Copyright (c) 2022-2023, The OTNS Authors. // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -36,10 +36,8 @@ import ( // based on an average RF propagation model. There is a hard stop of reception beyond the // radioRange of the node i.e. ideal disc model. type RadioModelIdeal struct { - Name string - UseVariableRssi bool // when true it uses distance-dependent RSSI model, else FixedRssi. - FixedRssi DbValue - Params *RadioModelParams + name string + params *RadioModelParams nodes map[NodeId]*RadioNode } @@ -63,9 +61,16 @@ func (rm *RadioModelIdeal) CheckRadioReachable(src *RadioNode, dst *RadioNode) b } func (rm *RadioModelIdeal) GetTxRssi(srcNode *RadioNode, dstNode *RadioNode) DbValue { - rssi := rm.FixedRssi // in the most ideal case, always assume a good RSSI up until the max range. - if rm.UseVariableRssi { - rssi = computeIndoorRssiItu(srcNode.GetDistanceTo(dstNode), srcNode.TxPower, rm.Params) + var rssi DbValue + if rm.params.RssiMinDbm < rm.params.RssiMaxDbm { + rssi = computeIndoorRssiItu(srcNode.GetDistanceTo(dstNode), srcNode.TxPower, rm.params) + if rssi < rm.params.RssiMinDbm { + rssi = rm.params.RssiMinDbm + } else if rssi > rm.params.RssiMaxDbm { + rssi = rm.params.RssiMaxDbm + } + } else { + rssi = rm.params.RssiMaxDbm } return rssi } @@ -98,7 +103,11 @@ func (rm *RadioModelIdeal) HandleEvent(node *RadioNode, q EventQueue, evt *Event } func (rm *RadioModelIdeal) GetName() string { - return rm.Name + return rm.name +} + +func (rm *RadioModelIdeal) GetParameters() *RadioModelParams { + return rm.params } func (rm *RadioModelIdeal) init() { diff --git a/radiomodel/radiomodelMutualInterference.go b/radiomodel/radiomodelMutualInterference.go index 2a0ac626..a268cd50 100644 --- a/radiomodel/radiomodelMutualInterference.go +++ b/radiomodel/radiomodelMutualInterference.go @@ -41,15 +41,8 @@ import ( // is also radio reception possible beyond the radioRange. Also, devices with better Rx sensitivity will receive // radio frames at longer distances beyond the radioRange. type RadioModelMutualInterference struct { - Name string - - // Whether RF signal reception is limited to the RadioRange disc of each node, or not (default false). - // If true, the interference (e.g. RSSI sampled on channel) is confined to the disc and frame - // reception is also confined to the disc. - IsDiscLimit bool - - // Parameters of an indoor propagation model - Params *RadioModelParams + name string + params *RadioModelParams shadowFading *shadowFading nodes map[NodeId]*RadioNode @@ -77,21 +70,33 @@ func (rm *RadioModelMutualInterference) CheckRadioReachable(src *RadioNode, dst if src == dst || dst.RadioState != RadioRx || src.RadioChannel != dst.RadioChannel { return false } - if rm.IsDiscLimit && src.GetDistanceTo(dst) > src.RadioRange { + if rm.params.IsDiscLimit && src.GetDistanceTo(dst) > src.RadioRange { return false } rssi := rm.GetTxRssi(src, dst) - floorDbm := math.Max(dst.RxSensitivity, rm.Params.NoiseFloorDbm) + rm.Params.SnrMinThresholdDb + floorDbm := math.Max(dst.RxSensitivity, rm.params.NoiseFloorDbm) + rm.params.SnrMinThresholdDb return rssi >= RssiMin && rssi <= RssiMax && rssi >= floorDbm } func (rm *RadioModelMutualInterference) GetTxRssi(src *RadioNode, dst *RadioNode) DbValue { + var rssi DbValue + dist := src.GetDistanceTo(dst) - if rm.IsDiscLimit && dist > src.RadioRange { + if rm.params.IsDiscLimit && dist > src.RadioRange { return RssiMinusInfinity } - rssi := computeIndoorRssi3gpp(dist, src.TxPower, rm.Params) - rssi -= rm.shadowFading.computeShadowFading(src, dst, rm.Params) + + if rm.params.RssiMinDbm < rm.params.RssiMaxDbm { + rssi = computeIndoorRssi3gpp(dist, src.TxPower, rm.params) + rssi -= rm.shadowFading.computeShadowFading(src, dst, rm.params) + if rssi < rm.params.RssiMinDbm { + rssi = rm.params.RssiMinDbm + } else if rssi > rm.params.RssiMaxDbm { + rssi = rm.params.RssiMaxDbm + } + } else { + rssi = rm.params.RssiMaxDbm + } return rssi } @@ -144,7 +149,11 @@ func (rm *RadioModelMutualInterference) HandleEvent(node *RadioNode, q EventQueu } func (rm *RadioModelMutualInterference) GetName() string { - return rm.Name + return rm.name +} + +func (rm *RadioModelMutualInterference) GetParameters() *RadioModelParams { + return rm.params } func (rm *RadioModelMutualInterference) init() { @@ -158,12 +167,12 @@ func (rm *RadioModelMutualInterference) init() { rm.interferedBy = map[NodeId]map[NodeId]*RadioNode{} } -func (rm *RadioModelMutualInterference) getRssiAmbientNoise(node *RadioNode, channel ChannelId) DbValue { - return rm.Params.NoiseFloorDbm +func (rm *RadioModelMutualInterference) getRssiAmbientNoise() DbValue { + return rm.params.NoiseFloorDbm } func (rm *RadioModelMutualInterference) getRssiOnChannel(node *RadioNode, channel ChannelId) DbValue { - rssiMax := rm.getRssiAmbientNoise(node, channel) + rssiMax := rm.getRssiAmbientNoise() // loop all active transmitters for _, v := range rm.activeTransmitters[channel] { rssi := rm.GetTxRssi(v, node) @@ -247,7 +256,7 @@ func (rm *RadioModelMutualInterference) txStop(node *RadioNode, evt *Event) { func (rm *RadioModelMutualInterference) applyInterference(src *RadioNode, dst *RadioNode, evt *Event) { // Apply interference. Loop all interferers that were active during Tx by 'src' and add their signal powers. - powIntfMax := rm.getRssiAmbientNoise(dst, ChannelId(evt.RadioCommData.Channel)) + powIntfMax := rm.getRssiAmbientNoise() for _, interferer := range rm.interferedBy[src.Id] { if interferer == dst { // if dst node was at some point transmitting itself, fail the Rx rm.log(evt.Timestamp, dst.Id, "Detected self-transmission of Node, set Rx OT_ERROR_ABORT")