A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
tv-spectrum-transmitter-helper.cc
Go to the documentation of this file.
1/*
2 * Copyright (c) 2014 University of Washington
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation;
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 *
17 * Author: Benjamin Cizdziel <ben.cizdziel@gmail.com>
18 */
19
21
22#include <ns3/double.h>
23#include <ns3/geographic-positions.h>
24#include <ns3/isotropic-antenna-model.h>
25#include <ns3/log.h>
26#include <ns3/mobility-helper.h>
27#include <ns3/position-allocator.h>
28#include <ns3/uinteger.h>
29
30#include <cmath>
31#include <list>
32#include <vector>
33
34namespace ns3
35{
36
37NS_LOG_COMPONENT_DEFINE("TvSpectrumTransmitterHelper");
38
42const double northAmericaStartFrequencies[84] = {
43 0, 0, 54e6, 60e6, 66e6, 76e6, 82e6, 174e6, 180e6, 186e6, 192e6, 198e6,
44 204e6, 210e6, 470e6, 476e6, 482e6, 488e6, 494e6, 500e6, 506e6, 512e6, 518e6, 524e6,
45 530e6, 536e6, 542e6, 548e6, 554e6, 560e6, 566e6, 572e6, 578e6, 584e6, 590e6, 596e6,
46 602e6, 608e6, 614e6, 620e6, 626e6, 632e6, 638e6, 644e6, 650e6, 656e6, 662e6, 668e6,
47 674e6, 680e6, 686e6, 692e6, 698e6, 704e6, 710e6, 716e6, 722e6, 728e6, 734e6, 740e6,
48 746e6, 752e6, 758e6, 764e6, 770e6, 776e6, 782e6, 788e6, 794e6, 800e6, 806e6, 812e6,
49 818e6, 824e6, 830e6, 836e6, 842e6, 848e6, 854e6, 860e6, 866e6, 872e6, 878e6, 884e6,
50};
52const double northAmericaEndFrequencies[84] = {
53 0, 0, 60e6, 66e6, 72e6, 82e6, 88e6, 180e6, 186e6, 192e6, 198e6, 204e6,
54 210e6, 216e6, 476e6, 482e6, 488e6, 494e6, 500e6, 506e6, 512e6, 518e6, 524e6, 530e6,
55 536e6, 542e6, 548e6, 554e6, 560e6, 566e6, 572e6, 578e6, 584e6, 590e6, 596e6, 602e6,
56 608e6, 614e6, 620e6, 626e6, 632e6, 638e6, 644e6, 650e6, 656e6, 662e6, 668e6, 674e6,
57 680e6, 686e6, 692e6, 698e6, 704e6, 710e6, 716e6, 722e6, 728e6, 734e6, 740e6, 746e6,
58 752e6, 758e6, 764e6, 770e6, 776e6, 782e6, 788e6, 794e6, 800e6, 806e6, 812e6, 818e6,
59 824e6, 830e6, 836e6, 842e6, 848e6, 854e6, 860e6, 866e6, 872e6, 878e6, 884e6, 890e6,
60};
61
63const int europeArrayLength = 70;
65const double europeStartFrequencies[70] = {
66 0, 0, 0, 0, 0, 174e6, 181e6, 188e6, 195e6, 202e6, 209e6, 216e6,
67 223e6, 0, 0, 0, 0, 0, 0, 0, 0, 470e6, 478e6, 486e6,
68 494e6, 502e6, 510e6, 518e6, 526e6, 534e6, 542e6, 550e6, 558e6, 566e6, 574e6, 582e6,
69 590e6, 598e6, 606e6, 614e6, 622e6, 630e6, 638e6, 646e6, 654e6, 662e6, 670e6, 678e6,
70 686e6, 694e6, 702e6, 710e6, 718e6, 726e6, 734e6, 742e6, 750e6, 758e6, 766e6, 774e6,
71 782e6, 790e6, 798e6, 806e6, 814e6, 822e6, 830e6, 838e6, 846e6, 854e6,
72};
74const double europeEndFrequencies[70] = {
75 0, 0, 0, 0, 0, 181e6, 188e6, 195e6, 202e6, 209e6, 216e6, 223e6,
76 230e6, 0, 0, 0, 0, 0, 0, 0, 0, 478e6, 486e6, 494e6,
77 502e6, 510e6, 518e6, 526e6, 534e6, 542e6, 550e6, 558e6, 566e6, 574e6, 582e6, 590e6,
78 598e6, 606e6, 614e6, 622e6, 630e6, 638e6, 646e6, 654e6, 662e6, 670e6, 678e6, 686e6,
79 694e6, 702e6, 710e6, 718e6, 726e6, 734e6, 742e6, 750e6, 758e6, 766e6, 774e6, 782e6,
80 790e6, 798e6, 806e6, 814e6, 822e6, 830e6, 838e6, 846e6, 854e6, 862e6,
81};
82
84const int japanArrayLength = 63;
86const double japanStartFrequencies[63] = {
87 0, 90e6, 96e6, 102e6, 170e6, 176e6, 182e6, 188e6, 192e6, 198e6, 204e6, 210e6, 216e6,
88 470e6, 476e6, 482e6, 488e6, 494e6, 500e6, 506e6, 512e6, 518e6, 524e6, 530e6, 536e6, 542e6,
89 548e6, 554e6, 560e6, 566e6, 572e6, 578e6, 584e6, 590e6, 596e6, 602e6, 608e6, 614e6, 620e6,
90 626e6, 632e6, 638e6, 644e6, 650e6, 656e6, 662e6, 668e6, 674e6, 680e6, 686e6, 692e6, 698e6,
91 704e6, 710e6, 716e6, 722e6, 728e6, 734e6, 740e6, 746e6, 752e6, 758e6, 764e6,
92};
94const double japanEndFrequencies[63] = {
95 0, 96e6, 102e6, 108e6, 176e6, 182e6, 188e6, 194e6, 198e6, 204e6, 210e6, 216e6, 222e6,
96 476e6, 482e6, 488e6, 494e6, 500e6, 506e6, 512e6, 518e6, 524e6, 530e6, 536e6, 542e6, 548e6,
97 554e6, 560e6, 566e6, 572e6, 578e6, 584e6, 590e6, 596e6, 602e6, 608e6, 614e6, 620e6, 626e6,
98 632e6, 638e6, 644e6, 650e6, 656e6, 662e6, 668e6, 674e6, 680e6, 686e6, 692e6, 698e6, 704e6,
99 710e6, 716e6, 722e6, 728e6, 734e6, 740e6, 746e6, 752e6, 758e6, 764e6, 770e6,
100};
101
103 : m_channel(nullptr),
105{
106 NS_LOG_FUNCTION(this);
107 m_factory.SetTypeId("ns3::TvSpectrumTransmitter");
108}
109
111{
112 m_channel = nullptr;
113 m_uniRand = nullptr;
114 NS_LOG_FUNCTION(this);
115}
116
117void
119{
120 NS_LOG_FUNCTION(this << c);
121 m_channel = c;
122}
123
124void
126{
127 m_factory.Set(name, val);
128}
129
132{
133 NS_LOG_FUNCTION(this);
134 NetDeviceContainer devCont;
135 // iterate over node container to make one transmitter for each given node
136 for (auto i = nodeCont.Begin(); i != nodeCont.End(); ++i)
137 {
138 Ptr<Node> node = *i;
140 phy->CreateTvPsd();
141 Ptr<NonCommunicatingNetDevice> dev = CreateObject<NonCommunicatingNetDevice>();
142 NS_ASSERT(phy);
143 dev->SetPhy(phy);
144 NS_ASSERT(node);
145 phy->SetMobility(node->GetObject<MobilityModel>());
146 NS_ASSERT(dev);
147 phy->SetDevice(dev);
149 phy->SetChannel(m_channel);
150 dev->SetChannel(m_channel);
151 node->AddDevice(dev);
152 devCont.Add(dev);
153 phy->Start();
154 }
155 return devCont;
156}
157
159TvSpectrumTransmitterHelper::Install(NodeContainer nodeCont, Region region, uint16_t channelNumber)
160{
161 NS_LOG_FUNCTION(this);
162 NetDeviceContainer devCont;
163 double startFrequency;
164 double channelBandwidth;
165 if (region == REGION_NORTH_AMERICA)
166 {
168 "channel number " << channelNumber << " does not exist for this region");
170 "channel number " << channelNumber << " does not exist for this region");
171 startFrequency = northAmericaStartFrequencies[channelNumber];
172 channelBandwidth =
173 northAmericaEndFrequencies[channelNumber] - northAmericaStartFrequencies[channelNumber];
174 }
175 else if (region == REGION_EUROPE)
176 {
177 NS_ASSERT_MSG(channelNumber < europeArrayLength,
178 "channel number " << channelNumber << " does not exist for this region");
179 NS_ASSERT_MSG(europeStartFrequencies[channelNumber] != 0,
180 "channel number " << channelNumber << " does not exist for this region");
181 startFrequency = europeStartFrequencies[channelNumber];
182 channelBandwidth =
183 europeEndFrequencies[channelNumber] - europeStartFrequencies[channelNumber];
184 }
185 else if (region == REGION_JAPAN)
186 {
187 NS_ASSERT_MSG(channelNumber < japanArrayLength,
188 "channel number " << channelNumber << " does not exist for this region");
189 NS_ASSERT_MSG(japanStartFrequencies[channelNumber] != 0,
190 "channel number " << channelNumber << " does not exist for this region");
191 startFrequency = japanStartFrequencies[channelNumber];
192 channelBandwidth =
193 japanEndFrequencies[channelNumber] - japanStartFrequencies[channelNumber];
194 }
195 // iterate over node container to make one transmitter for each given node
196 for (auto i = nodeCont.Begin(); i != nodeCont.End(); ++i)
197 {
198 Ptr<Node> node = *i;
200 phy->SetAttribute("StartFrequency", DoubleValue(startFrequency));
201 phy->SetAttribute("ChannelBandwidth", DoubleValue(channelBandwidth));
202 phy->CreateTvPsd();
203 Ptr<NonCommunicatingNetDevice> dev = CreateObject<NonCommunicatingNetDevice>();
204 NS_ASSERT(phy);
205 dev->SetPhy(phy);
206 NS_ASSERT(node);
207 phy->SetMobility(node->GetObject<MobilityModel>());
208 NS_ASSERT(dev);
209 phy->SetDevice(dev);
211 phy->SetChannel(m_channel);
212 dev->SetChannel(m_channel);
213 node->AddDevice(dev);
214 devCont.Add(dev);
215 phy->Start();
216 }
217 return devCont;
218}
219
222{
223 NS_LOG_FUNCTION(this);
224 NetDeviceContainer devCont;
225 int index = 0;
226 DoubleValue startFrequency;
227 DoubleValue channelBandwidth;
228 // iterate over node container to make one transmitter for each given node
229 for (auto i = nodeCont.Begin(); i != nodeCont.End(); ++i)
230 {
231 Ptr<Node> node = *i;
233 phy->GetAttribute("StartFrequency", startFrequency);
234 phy->GetAttribute("ChannelBandwidth", channelBandwidth);
235 phy->SetAttribute("StartFrequency",
236 DoubleValue(startFrequency.Get() + (index * channelBandwidth.Get())));
237 phy->CreateTvPsd();
238 Ptr<NonCommunicatingNetDevice> dev = CreateObject<NonCommunicatingNetDevice>();
239 NS_ASSERT(phy);
240 dev->SetPhy(phy);
241 NS_ASSERT(node);
242 phy->SetMobility(node->GetObject<MobilityModel>());
243 NS_ASSERT(dev);
244 phy->SetDevice(dev);
246 phy->SetChannel(m_channel);
247 dev->SetChannel(m_channel);
248 node->AddDevice(dev);
249 devCont.Add(dev);
250 phy->Start();
251 index++;
252 }
253 return devCont;
254}
255
258 Region region,
259 uint16_t channelNumber)
260{
261 NS_LOG_FUNCTION(this);
262 NetDeviceContainer devCont;
263 double startFrequency;
264 double channelBandwidth;
265 uint16_t currChannelNumber;
266 int index = 0;
267 // iterate over node container to make one transmitter for each given node
268 for (auto i = nodeCont.Begin(); i != nodeCont.End(); ++i)
269 {
270 currChannelNumber = channelNumber + index;
271 if (region == REGION_NORTH_AMERICA)
272 {
273 NS_ASSERT_MSG(currChannelNumber < northAmericaArrayLength,
274 "channel number " << currChannelNumber
275 << " does not exist for this region");
276 NS_ASSERT_MSG(northAmericaStartFrequencies[currChannelNumber] != 0,
277 "channel number " << currChannelNumber
278 << " does not exist for this region");
279 startFrequency = northAmericaStartFrequencies[currChannelNumber];
280 channelBandwidth = northAmericaEndFrequencies[currChannelNumber] -
281 northAmericaStartFrequencies[currChannelNumber];
282 }
283 else if (region == REGION_EUROPE)
284 {
285 NS_ASSERT_MSG(currChannelNumber < europeArrayLength,
286 "channel number " << currChannelNumber
287 << " does not exist for this region");
288 NS_ASSERT_MSG(europeStartFrequencies[currChannelNumber] != 0,
289 "channel number " << currChannelNumber
290 << " does not exist for this region");
291 startFrequency = europeStartFrequencies[currChannelNumber];
292 channelBandwidth =
293 europeEndFrequencies[currChannelNumber] - europeStartFrequencies[currChannelNumber];
294 }
295 else if (region == REGION_JAPAN)
296 {
297 NS_ASSERT_MSG(currChannelNumber < japanArrayLength,
298 "channel number " << currChannelNumber
299 << " does not exist for this region");
300 NS_ASSERT_MSG(japanStartFrequencies[currChannelNumber] != 0,
301 "channel number " << currChannelNumber
302 << " does not exist for this region");
303 startFrequency = japanStartFrequencies[currChannelNumber];
304 channelBandwidth =
305 japanEndFrequencies[currChannelNumber] - japanStartFrequencies[currChannelNumber];
306 }
307 Ptr<Node> node = *i;
309 phy->SetAttribute("StartFrequency", DoubleValue(startFrequency));
310 phy->SetAttribute("ChannelBandwidth", DoubleValue(channelBandwidth));
311 phy->CreateTvPsd();
312 Ptr<NonCommunicatingNetDevice> dev = CreateObject<NonCommunicatingNetDevice>();
313 NS_ASSERT(phy);
314 dev->SetPhy(phy);
315 NS_ASSERT(node);
316 phy->SetMobility(node->GetObject<MobilityModel>());
317 NS_ASSERT(dev);
318 phy->SetDevice(dev);
320 phy->SetChannel(m_channel);
321 dev->SetChannel(m_channel);
322 node->AddDevice(dev);
323 devCont.Add(dev);
324 phy->Start();
325 index++;
326 }
327 return devCont;
328}
329
330int64_t
332{
333 m_uniRand->SetStream(streamNum);
334 return 1;
335}
336
337void
339 Density density,
340 double originLatitude,
341 double originLongitude,
342 double maxAltitude,
343 double maxRadius)
344{
345 NS_LOG_FUNCTION(this);
346 std::list<int> transmitterIndicesToCreate;
347 if (region == REGION_NORTH_AMERICA)
348 {
349 transmitterIndicesToCreate =
352 density);
353 }
354 else if (region == REGION_EUROPE)
355 {
356 transmitterIndicesToCreate =
358 }
359 else if (region == REGION_JAPAN)
360 {
361 transmitterIndicesToCreate =
363 }
364 std::list<Vector> tvTransmitterLocations =
366 originLatitude,
367 originLongitude,
368 maxAltitude,
369 transmitterIndicesToCreate.size(),
370 maxRadius,
371 m_uniRand);
372 InstallRandomRegionalTransmitters(region, transmitterIndicesToCreate, tvTransmitterLocations);
373}
374
375std::list<int>
377 const int startFrequenciesLength,
378 Density density)
379{
380 std::vector<double> startFreqVector; // stores all non-zero start frequencies
381 for (int i = 0; i < startFrequenciesLength; i++)
382 {
383 double element = startFrequencies[i];
384 // add all non-zero frequencies to vector (0 means unused channel)
385 if (element != 0)
386 {
387 startFreqVector.push_back(element);
388 }
389 }
390
391 // randomly generate number of transmitters to create based on density
392 uint32_t freqVectorSize = startFreqVector.size();
393 int randNumTransmitters = GetRandomNumTransmitters(density, freqVectorSize);
394
395 // stores start frequencies that transmitters will be created to transmit
396 std::vector<double> transmitterStartFreqsToCreate;
397 for (int i = 0; i < randNumTransmitters; i++)
398 {
399 // get random index from start frequency vector
400 uint32_t randIndex = m_uniRand->GetInteger(0, startFreqVector.size() - 1);
401 // add start frequency corresponding to random index to vector
402 transmitterStartFreqsToCreate.push_back(startFreqVector[randIndex]);
403 // remove selected start frequency from vector so it is not selected again
404 startFreqVector.erase(startFreqVector.begin() + randIndex);
405 }
406
407 // find indices on startFrequencies[] containing each start frequency that is
408 // selected to be transmitted and add to list
409 std::list<int> transmitterIndicesToCreate;
410 for (int i = 0; i < (int)transmitterStartFreqsToCreate.size(); i++)
411 {
412 for (int channelNumberIndex = 0; channelNumberIndex < startFrequenciesLength;
413 channelNumberIndex++)
414 {
415 if (startFrequencies[channelNumberIndex] == transmitterStartFreqsToCreate[i])
416 {
417 transmitterIndicesToCreate.push_back(channelNumberIndex);
418 break;
419 }
420 }
421 }
422 return transmitterIndicesToCreate;
423}
424
425int
427{
428 int numTransmitters;
429 if (density == DENSITY_LOW)
430 {
431 numTransmitters = m_uniRand->GetInteger(1, ceil(0.33 * numChannels));
432 }
433 else if (density == DENSITY_MEDIUM)
434 {
435 numTransmitters =
436 m_uniRand->GetInteger(ceil(0.33 * numChannels) + 1, ceil(0.66 * numChannels));
437 }
438 else
439 {
440 numTransmitters = m_uniRand->GetInteger(ceil(0.66 * numChannels) + 1, numChannels);
441 }
442 return numTransmitters;
443}
444
445void
447 Region region,
448 std::list<int> transmitterIndicesToCreate,
449 std::list<Vector> transmitterLocations)
450{
451 int numTransmitters = (int)transmitterIndicesToCreate.size();
452 for (int transNum = 0; transNum < numTransmitters; transNum++)
453 {
454 Ptr<ListPositionAllocator> nodePosition = CreateObject<ListPositionAllocator>();
455 // add generated coordinate point to node position
456 nodePosition->Add(transmitterLocations.front());
457 MobilityHelper mobility;
458 mobility.SetPositionAllocator(nodePosition);
459 mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel");
460 NodeContainer tvNode; // contains position of transmitter to be created
461 tvNode.Create(1);
462 mobility.Install(tvNode);
463 // set channel number for this transmitter
464 auto channelNumber = (uint16_t)transmitterIndicesToCreate.front();
465 Install(tvNode, region, channelNumber); // install tv transmitter
466 transmitterLocations.pop_front(); // remove created transmitter location
467 transmitterIndicesToCreate.pop_front(); // remove created transmitter index
468 }
469}
470
471} // namespace ns3
Hold a value for an Attribute.
Definition: attribute.h:70
This class can be used to hold variables of floating point type such as 'double' or 'float'.
Definition: double.h:42
double Get() const
Definition: double.cc:37
static std::list< Vector > RandCartesianPointsAroundGeographicPoint(double originLatitude, double originLongitude, double maxAltitude, int numPoints, double maxDistFromOrigin, Ptr< UniformRandomVariable > uniRand)
Generates uniformly distributed random points (in ECEF Cartesian coordinates) within a given altitude...
Helper class used to assign positions and mobility models to nodes.
Keep track of the current position and velocity of an object.
holds a vector of ns3::NetDevice pointers
void Add(NetDeviceContainer other)
Append the contents of another NetDeviceContainer to the end of this container.
keep track of a set of node pointers.
Iterator End() const
Get an iterator which indicates past-the-last Node in the container.
void Create(uint32_t n)
Create n nodes and append pointers to them to the end of this NodeContainer.
Iterator Begin() const
Get an iterator which refers to the first Node in the container.
Ptr< Object > Create() const
Create an Object instance of the configured TypeId.
void Set(const std::string &name, const AttributeValue &value, Args &&... args)
Set an attribute to be set during construction.
void SetTypeId(TypeId tid)
Set the TypeId of the Objects to be created by this factory.
Ptr< T > GetObject() const
Get a pointer to the requested aggregated Object.
Definition: object.h:522
Smart pointer class similar to boost::intrusive_ptr.
Definition: ptr.h:77
void SetStream(int64_t stream)
Specifies the stream number for the RngStream.
std::list< int > GenerateRegionalTransmitterIndices(const double startFrequencies[], const int startFrequenciesLength, Density density)
Generates random indices of given region frequency array (ignoring indices referring to invalid chann...
void SetChannel(Ptr< SpectrumChannel > c)
Set the spectrum channel for the device(s) to transmit on.
NetDeviceContainer InstallAdjacent(NodeContainer nodes)
Set up and start the TV Transmitter's transmission on the spectrum channel.
Density
density of location that TV transmitters are being set up in
void InstallRandomRegionalTransmitters(Region region, std::list< int > transmitterIndicesToCreate, std::list< Vector > transmitterLocations)
Installs each randomly generated regional TV transmitter.
Region
geographical region that TV transmitters are being set up in
int GetRandomNumTransmitters(Density density, uint32_t numChannels)
Randomly generates the number of TV transmitters to be created based on given density and number of p...
Ptr< UniformRandomVariable > m_uniRand
Object to generate uniform random numbers.
int64_t AssignStreams(int64_t streamNum)
Assigns the stream number for the uniform random number generator to use.
ObjectFactory m_factory
Object factory for attribute setting.
void CreateRegionalTvTransmitters(Region region, Density density, double originLatitude, double originLongitude, double maxAltitude, double maxRadius)
Generates and installs (starts transmission on the spectrum channel) a random number of TV transmitte...
void SetAttribute(std::string name, const AttributeValue &val)
Set attribute for each TvSpectrumTransmitter instance to be created.
NetDeviceContainer Install(NodeContainer nodes)
Set up and start the TV Transmitter's transmission on the spectrum channel.
Ptr< SpectrumChannel > m_channel
Pointer to spectrum channel object.
SpectrumPhy implementation that creates a customizable TV transmitter which transmits a PSD spectrum ...
The uniform distribution Random Number Generator (RNG).
uint32_t GetInteger(uint32_t min, uint32_t max)
Get the next random value drawn from the distribution.
#define NS_ASSERT(condition)
At runtime, in debugging builds, if this condition is not true, the program prints the source file,...
Definition: assert.h:66
#define NS_ASSERT_MSG(condition, message)
At runtime, in debugging builds, if this condition is not true, the program prints the message to out...
Definition: assert.h:86
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:202
#define NS_LOG_FUNCTION(parameters)
If log level LOG_FUNCTION is enabled, this macro will output all input parameters separated by ",...
Ptr< T > CreateObject(Args &&... args)
Create an object by type, with varying number of constructor parameters.
Definition: object.h:630
Every class exported by the ns3 library is enclosed in the ns3 namespace.
const double northAmericaEndFrequencies[84]
NORTH AMERICA end frequencies.
const int japanArrayLength
JAPAN: 63 elements (index 0 - 62); valid channels = 1 - 62.
const double europeEndFrequencies[70]
EUROPE end frequencies.
const int northAmericaArrayLength
NORTH AMERICA: 84 elements (index 0 - 83); valid channels = 2 - 83.
const double japanStartFrequencies[63]
JAPAN start frequencies.
const double europeStartFrequencies[70]
EUROPE start frequencies.
const double japanEndFrequencies[63]
JAPAN end frequencies.
const double northAmericaStartFrequencies[84]
NORTH AMERICA start frequencies.
const int europeArrayLength
EUROPE: 70 elements (index 0 - 69); valid channels = 5 - 12, 21 - 69.