RF testing on WFx is achieved using the wfx_test_dut python3 module, available in the wfx-common-tools repository 'test-feature' folder.
NB: wfx_test_dut is an evolution of the initial test-feature scripts which were present in the wfx-linux-tools repository 'test-feature' folder, with the added capability to be ran from a test server with either SSH, UART or TELNET access to the DUT. Only the Linux wfx_test_agent (discussed below) is now present under the wfx-linux-tools repository 'test-feature' folder.
The RF test architecture is now a Test Server/DUT configuration
- The Test server can run wfx_test_dut on any python3-capable platform with network and/or UART communication capabilities
- One possible python3-capable platform is the Raspberry PI. In this case, both the Test Server and the DUT are running on the Raspberry PI, if this is convenient for testing.
- The DUT only needs to support a small wfx_test_agent executable with the following capabilities:
write_test_data [data_string]
(required for Tx and Rx testing)read_rx_stats
(required only for Rx testing)read_agent_version
(optional, used for logging test conditions)read_driver_version
(optional, used for logging test conditions)read_fw_version
(optional, used for logging test conditions)read_tx_info
(optional, used with a FEM)
NB: Prior to running RF testing, make sure you stop any WLAN use of your product.
First, install the WXF connection layer
The connection layer is common to RF testing, allowing connection in the following modes:
- Local
- SSH (with password of public key)
- UART
(The connection layer is available in wfx-common-tools repository 'connection' folder, so from the RF Test scripts perspective they are under ../connection
)
Refer to the connection README for details on the connection layer and its installation.
The wfx test agent needs to be installed on the DUT
- Installation differs depending on the platform
-
Download from the wfx-linux-tools repository 'test-feature' folder the Linux wfx_test_agent
-
The Linux agent is directly usable on Linux platforms
- make sure it has execution rights and is in the path (create a link as /usr/local/bin/wfx_test_agent)
chmod a+x /home/pi/siliconlabs/wfx-linux-tools/test-feature/wfx_test_agent
sudo ln -sf /home/pi/siliconlabs/wfx-linux-tools/test-feature/wfx_test_agent /usr/local/bin/wfx_test_agent
- The RTOS and Bare Metal agents need to be adapted and be compiled for your platform
The wfx_test_agent can be tested & validated stand-alone before being used for RF Testing:
- On Linux platforms, call
wfx_test_agent <option>
to test all options - On platforms accessible via SSH, open a SSH session and call
wfx_test_agent <option>
to test all options - On platforms accessible via UART, open a terminal and call
wfx_test_agent <option>
to test all options
Executing commands on the DUT is possible using the dut.run(cmd) syntax
An example is checking the wfx_test_agent version:
dut.run('wfx_test_agent read_agent_version')
Any other DUT command can also be called in a similar way. For instance, the following would work on a Linux DUT
dut.run('uname -r')
Some DUT wfx_test_agent features are mandatory for RF Testing:
write_test_data [data_string]
(required for Tx and Rx testing)- Send [data_string] as a HI_CONFIGURATION_REQ_ID request message payload
read_rx_stats
(required only for Rx testing)- Return the payload of a HI_GENERIC_INDICATION_ID_RX_STATS indication message formatted as follows (abstract):
Timestamp: 43158786us
Low power clock: frequency 32759Hz, external yes
Num. of frames: 73, PER (x10e4): 1780, Throughput: 63Kbps/s
Num. of PER RSSI SNR CFO
frames (x10e4) (dBm) (dB) (kHz)
1M 62 322 -76 17 -26
2M 0 0 0 0 0
. . .
54M 1 10000 -90 4 65
MCS0 0 0 0 0 0
. . .
MCS7 0 0 0 0 0
Others are only useful to log test conditions:
read_agent_version
(returns '1.0.0' at the time of writing)read_driver_version
(returns '2.0.3' at the time of writing)read_fw_version
(returns '2.2.1' at the time of writing)
Others are used with a FEM:
read_tx_info
Tx gain digital: 0
Tx gain PA: 0
Target Pout: 0.00 dBm
FEM Pout: 0.00 dBm
Vpdet: 0 mV
Measure index: 0
The wfx_test_agent needs to be executed with root privileges over SSH.
To achieve this on platforms the root user can be accessed using a password, use the root account to connect the DUT.
The Raspberry Pi does not allow a root user to be accessed using a password, so the necessary steps are required (once):
- Copying the server's public key to the 'pi' user's account
- Logging as user 'pi'
- As user Pi, use 'sudo' to copy the public key from the 'pi' user's account to the root user account.
Following this, access to Raspberry PIs with root privileges will be possible using user='root, host = '<pi_address>' when connecting the DUT (with no password)
Thanks to a contribution from @gsalvatella, it is now possible to provide the path to a SSH public key (as the 'pkey' parameters) when using SSH. Use this when running the tools on your target.
>>> from wfx_common_tools.test_feature import wfx_test_dut
>>> dut = wfx_test_dut.WfxTestDut('Local')
>>> dut = wfx_test_dut.WfxTestDut('Pi_186', host='10.5.124.186', user='root', port=22)
NB: for SSH connection: user, port and password values are optional, values used above are the default values (The user account needs to have root privileges)
SSH providing public key:
>>> dut = wfx_test_dut.WfxTestDut('Pi_186', host='10.5.124.186', user='root', port=22, pkey=<path_to_SSH_public_key>)
>>> dut = wfx_test_dut.WfxTestDut('Serial', port='COM21', baudrate=115200, bytesize=8, parity='N', stopbits=1, user='<user>', password='<password>' )
NB: for UART connection: baudrate, bytesize, parity and stopbits values are optional, values used above are the default values
>>> dut = wfx_test_dut.WfxTestDut('Serial', port='COM21', baudrate=115200, bytesize=8, parity='N', stopbits=1)
NB: for UART connection: baudrate, bytesize, parity and stopbits values are optional, values used above are the default values
>>> dut.tx_rx_select(1,1)
>>> dut.channel(11)
>>> dut.tx_mode('GF_MCS0')
>>> dut.tx_framing(packet_length_bytes, ifs_us)
>>> dut.tx_start('continuous')
. . .
>>> dut.tx_stop()
While testing, messages are issued in dmesg every TEST_IND period (in ms):
wfx_wlan: Start TX packet test feature
wfx_wlan: TX packet test ongoing...
wfx_wlan: TX packet test ongoing...
. . .
wfx_wlan: End of TX packet test
wfx_wlan: Start TX packet test feature
wfx_wlan: End of TX packet test
NB: The last 2 lines correspond to the stop()
call, which is sending 100 frames
NB: test_ind_period()
allows controlling the delay between these messages
>>> dut.tx_rx_select(1,1)
>>> dut.channel(11)
>>> dut.tone_power(dbm)
>>> dut.tone_freq(freq)
>>> dut.tone_start([freq])
. . .
>>> dut.tone_stop()
While testing, messages are issued in dmesg every TEST_IND period (in ms):
wfx_wlan: Start TX CW test feature
wfx_wlan: TX CW test ongoing...
wfx_wlan: TX CW test ongoing...
. . .
wfx_wlan: End of TX CW test
wfx_wlan: Start TX packet test feature
wfx_wlan: End of TX packet test
NB: The last 2 lines correspond to the tone_stop()
call, which is sending 100 frames
NB: dut.test_ind_period()
allows controlling the delay between these messages
>>> dut.tx_rx_select(1,1)
>>> dut.channel(11)
>>> dut.rx_start()
>>> dut.rx_receive('MCS7', frames=10000, timeout_s = 10)
>>> dut.rx_logs('global')
>>> dut.rx_logs('MCS7')
>>> dut.rx_logs()
. . .
>>> dut.rx_stop()
While testing, an rx_stats indication message is updated by the FW every test_ind_period
(default 1000 ms), and transmitted to the driver as a HI_GENERIC_INDICATION_ID_RX_STATS indication message.
- Under Linux, it is formatted and copied by the driver under
/sys/kernel/debug/ieee80211/phy*/wfx/rx_stats
. - For non-Linux platforms, the wfx_test_agent should format the HI_GENERIC_INDICATION_ID_RX_STATS indication message
to the same format and return it when called with option
'read_rx_stats
.
This content is polled by dut.rx_receive()
, results are accumulated and averaged
internally by the Python code.
The results are retrieved by the user using dut.rx_logs()
under the following form:
- 'global' results
>>> dut.rx_logs('global')
frames 588 errors 116 PER 1.973e-01 Throughput 78 deltaT 10000057 loops 10 start_us 2245737 last_us 12245794
- results for a selected modcode:
>>> dut.rx_logs('24M')
frames 76 errors 48 PER 6.316e-01 RSSI -79 SNR 7 CFO -18
- all results
>>> dut.rx_logs():
mode global frames 588 errors 116 PER 1.973e-01 Throughput 78 deltaT 10000057 loops 10 start_us 2245737 last_us 12245794
mode 1M frames 457 errors 14 PER 3.063e-02 RSSI -77 SNR 10 CFO -28
mode 2M frames 0 errors 0 PER 1.000e+00 RSSI 0 SNR 0 CFO 0
mode 5.5M frames 0 errors 0 PER 1.000e+00 RSSI 0 SNR 0 CFO 0
mode 11M frames 0 errors 0 PER 1.000e+00 RSSI 0 SNR 0 CFO 0
mode 6M frames 4 errors 3 PER 7.500e-01 RSSI -78 SNR 8 CFO -22
mode 9M frames 10 errors 10 PER 1.000e+00 RSSI -82 SNR 3 CFO 34
mode 12M frames 9 errors 9 PER 1.000e+00 RSSI -83 SNR 5 CFO 19
mode 18M frames 5 errors 5 PER 1.000e+00 RSSI -83 SNR 7 CFO 18
mode 24M frames 76 errors 48 PER 6.316e-01 RSSI -79 SNR 7 CFO -18
mode 36M frames 4 errors 4 PER 1.000e+00 RSSI -76 SNR 9 CFO -22
mode 48M frames 7 errors 7 PER 1.000e+00 RSSI -84 SNR 2 CFO -2
mode 54M frames 7 errors 7 PER 1.000e+00 RSSI -84 SNR 2 CFO -27
mode MCS0 frames 0 errors 0 PER 1.000e+00 RSSI 0 SNR 0 CFO 0
mode MCS1 frames 0 errors 0 PER 1.000e+00 RSSI 0 SNR 0 CFO 0
mode MCS2 frames 1 errors 1 PER 1.000e+00 RSSI -80 SNR 9 CFO -20
mode MCS3 frames 8 errors 8 PER 1.000e+00 RSSI -80 SNR 9 CFO -23
mode MCS4 frames 0 errors 0 PER 1.000e+00 RSSI 0 SNR 0 CFO 0
mode MCS5 frames 0 errors 0 PER 1.000e+00 RSSI 0 SNR 0 CFO 0
mode MCS6 frames 0 errors 0 PER 1.000e+00 RSSI 0 SNR 0 CFO 0
mode MCS7 frames 0 errors 0 PER 1.000e+00 RSSI 0 SNR 0 CFO 0
NB: PER values above are only considering received packets, they do not take into account lost packets. To get a PER taking into account the lost packets it is necessary to compute the total number of packets for each mode using the deltaT value and the source settings. This is closely related to the equipment used to generate the source signal, with is specific to each test setup.
The test data structure as filled before calling pds_compress is as follows:
RF_ANTENNA_SEL_DIV_CFG : {
RF_PORTS : TX1_RX1,
},
MAX_TX_POWER_CFG : {
FRONT_END_LOSS_CORRECTION_QDB : 0,
MAX_OUTPUT_POWER_QDBM : 80,
BACKOFF_QDB : [ {
CHANNEL_NUMBER : [1, 14],
BACKOFF_VAL : [0, 0, 0, 0, 0 ,0],
} ],
RF_PORT : RF_PORT_BOTH,
},
TEST_FEATURE_CFG : {
RX : { },
CFG_TX_PACKET : {
REG_MODE : DFS_Unrestricted,
IFS_US : 0,
RATE : N_MCS7,
NB_FRAME : 0,
FRAME_SIZE_BYTE : 3000,
HT_PARAM : MM,
},
CFG_TX_CW : {
FREQ1 : 1,
FREQ2 : 2,
CW_MODE : single,
MAX_OUTPUT_POWER : 68,
},
TEST_IND : 1000,
TEST_CHANNEL_FREQ : 11,
TEST_MODE : tx_packet,
},
wfx_connection.py manages the connectivity between the Tester and the DUT
uarts()
: list the existing uarts on the Tester which can be used to connect to DUTs over RS232networks()
: list the existing IP networks on the Tester which can be used to connect to DUTs over SSHwrite(text)
: write the input text to the DUTread(text)
: read the DUT replyrun(cmd, wait_ms=0)
: callwrite(cmd)
, pause for wait_ms, then callread()
to retrieve the DUT answer- class Uart is implementing the above functions for UART DUTs
- class Ssh is implementing the above functions for SSH DUTs
- class Direct is implementing the above functions for Direct/Local DUTs, i.e. those connected directly when the Tester is also the DUT
pds_compress.py is a copy of the pds_compress tool used to compress the PDS data
(the one sent right after FW download) with the added .py
extension, such that it can be loaded as a Python3 module. It is much faster to load this module once and for all than calling it from the OS every time test data needs to be 'compressed'.
wfx_pds_tree.py manages the Test data in a nested dict
wfx_pds{} defines the Test data structure
ITEM
names are unique by design (FW constraint)VERSION
defines the mimimal FW versionPATH
defines the position of each item in the treeDEFAULT
defines the default valueVALUES
lists possible values for each itemDOC
contains the documentation relative to each item
NB: The line order defines in which order sections are to be sent (when sending several sections) as well as in which order items within a section wil be ordered_
fill_tree(version)
: Fills the tree, adding only items supported by the current FW.get(key)
: Gets an item value, wherever it is in the tree.pretty()
: Returns tabulated Test data (much easier to read than print() output).print()
: Returns one-line Test data.set(key, value)
: Sets an item to a new value, wherever it is in the tree.set_current_fw_version(version)
: Stores the current FW version (retrieved from HW by upper layers).sub_tree(keys)
: Returns a copy of the Test data, with only (entire) sections matching the selected keys. Used to avoid sending entire test data on each 'send'.
Additional PdsTree functions
add_tmp_param("version", "path", "key", "default")
: Add a (temporary) parameter to the test data structure. Useful to test FW release candidates in the lab. Most probably not relevant for customers
Additional wfx_pds_tree functions
add_pds_warning(msg)
: accumulate error messages related to test data processing.check_pds_warning(msg="")
: return accumulated error messages related to test data processing. Clear previous messages before returning. If no error, returnmsg
wfx_test_target.py relies on
- wfx_connection.py to communicate with the DUT
- wfx_pds_tree.py to store and manage test data
- pds_compress.py to compress test data
write(text)
: write the input text to the DUTread(text)
: read the DUT replyrun(cmd, wait_ms=0)
: callwrite(cmd)
, pause for wait_ms, then callread()
to get DUT answerwfx_get_list(param_list)
: Returns a name/value sequence for all selected items.wfx_set_dict(param_dict, send_data)
: Sets all selected items to their desired values, compress the test data tree and send the result (send only if send_data == 1(default)).
wfx_test_dut.py translates the test API user function calls into test data and sends it. These are the functions which are primarily used by users to test the product.
NB: When called with no argument, most functions return the corresponding value(s) of the parameter(s) they control*
wfx_test_dut.py relies on
- wfx_test_target.py to handle the test data and communicate with the DUT
function | usage | function parameters |
---|---|---|
add_tmp_param |
adds a temporary parameter to the tree (for debug purposes) | version : min FWpath : position in treekey : namedefault :value |
c_tune_xi_xo(xi, xo) |
set XTAL capacitance (see UG404 for details) | xi : [0-255]xo : [0-255] XTAL capacitance |
c_tune_fix(fix) |
configure XTAL imbalance (see UG404 for details) | fix : [0-3] XTAL imbalance configuration |
channel(ch) |
set the test channel | ch : [1-14](channel) or [2300-2530] MHz |
fem_pa_max_gain(gain_db) |
set FEM Power Amplifier max gain in closed loop set FEM Power Amplifier typical gain in open loop |
gain_db : [0-256]. Max FEM Power Amplifier Gain in dB |
fem_pa_table(vdet_vs_pout) |
set or check the FEM PA table | vdet_vs_pout :'[[<vdet>, <pout>], ...]': Up to 16 [vdet, pout] pairs 'open_loop': start open loop mode 'closed_loop' start closed loop mode 'text': returns series of vdet and pout values, number of points and indicates the current loop mode |
fem_pa_used(yes_no) |
activates/de-activates the FEM Power Amplifier | yes_no : ['yes', 'no']none: check the current FEM Amplifier state |
fem_read_digital_gain() |
Returns WFx digital gain info | none |
fem_read_fem_pout() |
Returns FEM output power estimated by firmware using FEM PA table and Vpdet measured, in dBm | none |
fem_read_measure_index() |
Returns 8 bit counter of Vpdet measurements, refreshed every second | none |
fem_read_pa_slice() |
Returns WFx PA slice | none |
fem_read_target_pout() |
Returns requested target output power, in dBm | none |
fem_read_tx_info(match) |
Returns FEM tx info * WFx digital gain info and PA slice * FEM target output power * voltage measured at WFx VPDET pin * output power estimated by firmware * 8 bit counter of Vpdet measurements refreshed every second |
if no parameter, returns the entire text as formatted by the driver if match is provided, filter on match . If match is 'values', return a series of name/value pairs |
fem_read_vpdet() |
Returns voltage measured on VPDET in mV | none |
read_agent_version() |
returns Agent version | none |
read_driver_version() |
returns driver version | none |
read_fw_version() |
returns FW version | none |
read_rx_stats() |
call run('wfx_test_agent read_rx_start') to retrieve the Rx stats |
none |
regulatory_mode(reg_mode) |
applies TX power backoff for the selected region | reg_mode :'[All, FCC, ETSI, JAPAN, Unrestricted]' |
rx_logs(mode=None) |
retrieve accumulated python content of Rx stats polling. Full table if no 'mode', otherwise only the row matching the 'mode' (logs are available until next tx_receive() call) |
mode : 'global'(default if '') '[1, 2, 5.5, 11, 6, 9, 12, 18, 24, 36, 48, 54]M' 'MCS[0-7]' |
rx_receive(mode, frames, sleep_ms, timeout) |
Clear Rx logs and polls Rx stats until it has received the required number of frames(default 1000). | mode :'endless': run continuously 'global'(default if '') '[1, 2, 5.5, 11, 6, 9, 12, 18, 24, 36, 48, 54]M' 'MCS[0-7]' frames : Nb of frames to receive before stopping'sleep_ms :[(750)]. Polling period. No need to poll too often, the FW updates the table only every secondtimeout_s : max number of seconds to poll (useful if very few frames are received) |
rx_start() |
start Rx test in the DUT | none |
rx_stop() |
stop Rx test in the DUT and Python3 polling thread | none |
test_conditions() |
returns DUT/Driver/FW/Tools/Agent versions and DUT connection info | none |
test_ind_period() |
set the delay between indication messages in Tx and Rx_stats in | period : period in ms before 2 status indications |
tone_freq(offset=None) |
set CW tone offset | offset : offset([-31,31], default 0) in 312.5 kHz steps |
tone_power(dBm) |
set tone power | dbm : [TBD] |
tone_start(offset=None) |
start CW tone on current channel | offset : offset([-31,31], default 0) in 312.5 kHz steps |
tone_stop() |
stop CW tone | none |
tx_backoff(mod, backoff_level) |
set power backoff for one group of modulations. All other backoff 0. Used during certification testing to find the backoff levels to be set in the production PDS to stay within regulatory power limits |
mode_802_11 :'[B]_[1, 2, 5_5, 11]Mbps' '[G]_[6, 9, 12, 18, 24, 36, 48, 54]Mbps' '[MM, GF]_MCS[0-7]' Examples: 'B_1Mbps', 'G_54Mbps', 'GF_MCS5' backoff_level : [0:63.75] dB |
tx_framing(pkt_len, ifs_us) |
control the frame size (in bytes) and IFS (InterFrame Spacing) | packet_length_bytes :[25-4091] Frame size in bytes(without CRC)ifs_us :[0-255] Interframe spacing in us. 100us is recommended for WFM200 to allow crystal compensation over temperature |
tx_mode(mode) |
select between B (11b), G (11g), MM (11n mixed mode) & GF (11n Greenfield) and set the rate | mode_802_11 :'[B]_[1, 2, 5_5, 11]Mbps' '[G]_[6, 9, 12, 18, 24, 36, 48, 54]Mbps' '[MM, GF]_MCS[0-7]' Examples: 'B_1Mbps', 'G_54Mbps', 'GF_MCS5' |
tx_power(dBm) |
set the maximum output power. NB: use tx_backoff() for certification testing |
dbm : [TBD] |
tx_rx_select(tx_ant, rx_ant) |
select the Tx/Rx antennas | tx_ant : [1-2] Tx antennarx_ant : [1-2] Rx antenna |
tx_start(nb_frames) |
start sending a selected number of frames | nb_frames : [0-65535] or 'continuous'. Nb of frames to send before stopping.0 = 'continuous' |
tx_stop() |
send a burst of 100 frames to complete a previous continuous transmission | none |
pretty (tabulated) | single line |
---|---|
print(dut.test_data.pretty()) |
print(dut.test_data.print()) |
Nb: the above is only possible if using dut = WfxTestDut(...)
.
RF Tx certification testing of a Wi-Fi product consists in checking that Tx signals are compliant with regulatory requirements (power, spurious,..) without using an AP in WLAN mode.
Refer to Certification Testing for details and an example.
use dut.trace = True
to trace all changes to test data items
use dut.link.trace = True
to trace all communication with the wfx_test-agent
All parameters (even those not managed by test functions) listed in the wfx_test_dut API are still accessible using generic functions:
>>> dut.test_data.wfx_get_list({'NB_FRAME'})
>>> dut.test_data.wfx_get_list({'TEST_MODE','NB_FRAME'})
>>> dut.test_data.wfx_set_dict({'NB_FRAME':12}, 0)
>>> dut.test_data.wfx_set_dict({'TEST_MODE':'tx_packet','NB_FRAME':12}, 0)
>>> dut.test_data.wfx_set_dict({'NB_FRAME':12}, 1)
>>> dut.test_data.wfx_set_dict({'TEST_MODE':'tx_packet','NB_FRAME':12}, 1)
It is possible to define new parameters and access them using the generic
wfx_get_list
/ wfx_set_dict
functions described above.
>>> dut.test_data.add_tmp_param('version', 'path', 'key', 'default')
adds a (temporary) parameter to the Test structure. This is useful to test FW release candidates in the lab
- example
# Creating the params in the tree (Pending FW support for 'z.a.b.x' & 'z.a.b.y'):
>>> dut.test_data.add_tmp_param(pds, '2.0', 'z.a.b', 'x', '10')
>>> dut.test_data.add_tmp_param(pds, '2.0', 'z.a.b', 'y', '25')
# Setting the value and sending Test data:
>>> dut.test_data.wfx_set_dict({'x':15, 'y':32}, 1)
Below is an example of testing directly on the Raspberry PI, having the PI as both RF Test Server and DUT
python3
Python 3.4.2 (default, Oct 19 2014, 13:31:11)
[GCC 4.9.1] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from wfx_test_dut import *
/usr/local/lib/python3.4/dist-packages/cryptography/hazmat/bindings/openssl/binding.py:163:
CryptographyDeprecationWarning: OpenSSL version 1.0.1 is no longer supported by the OpenSSL project, please upgrade. A future version of cryptography will drop support for it.
utils.CryptographyDeprecationWarning
Disregard the CryptographyDeprecationWarning above, since it's related to paramiko dependencies, and dealing with these it out of our scope (as long as it still works)
>>> dut = WfxTestDut('Local')
Local: Configuring a Direct connection
Local D>>| wfx_test_agent read_fw_version
<<D Local| 2.2.1
The lines with Local D>>|
or <<D Local|
are write/read traces coming from the connection link.
These can be disabled using dut.link.trace = False
. They are useful to check/debug the wfx_test_agent behavior.
Local: fw_version retrieved from HW (2.2.1)
Info: 'FRONT_END_LOSS_RX_QDB' cannot be supported with FW2.2.1, it has been added in FW2.2.2 (skipped)
The above information tells us that the current FW is 2.2.1. In wfx_pds{} the VERSION value for 'FRONT_END_LOSS_RX_QDB' is 2.2.2. This means that the current FW doesn't support 'FRONT_END_LOSS_RX_QDB', so it's not added to the test_data we will use here, to avoid FW exceptions
fill_tree has messages:
Info: 'FRONT_END_LOSS_RX_QDB' cannot be supported with FW2.2.1, it has been added in FW2.2.2 (skipped)
After filling the test_data tree, the code checks for any test data processing message (using check_pds_warning()
),
so the message is first printed when it occurs then following this check.
Local: tree filled for FW2.2.1
Finally, we get a message indicating the FW version used by the test data.
Local D>>| wfx_test_agent read_agent_version
<<D Local| 1.0.0
Direct agent_reply: 1.0.0
>>> dut.test_conditions()
Local D>>| wfx_test_agent read_agent_version
<<D Local| 1.0.0
Local D>>| wfx_test_agent read_fw_version
<<D Local| 2.2.1
Local D>>| wfx_test_agent read_driver_version
<<D Local| 2.0.3
'Test conditions: DUT Local / Driver 2.0.3 / FW 2.2.1 / Tools 1.0.0 / Agent 1.0.0 / Direct'
>>>
Here we see the wfx_test_agent in action, replying to options used to retrieve the test conditions
>>> dut.channel(7)
Local SET| TEST_CHANNEL_FREQ 7
'TEST_CHANNEL_FREQ 7'
>>> dut.tx_start('continuous')
Local D>>| wfx_test_agent write_test_data "{i:{a:7,b:1,f:3E8,c:{a:0,b:1,c:2,d:44},d:{a:BB8,b:0,c:0,d:15,e:0,f:4},e:{}}}"
<<D Local| '{i:{a:7,b:1,f:3E8,c:{a:0,b:1,c:2,d:44},d:{a:BB8,b:0,c:0,d:15,e:0,f:4},e:{}}}' sent to /sys/kernel/debug/ieee80211/phy0/wfx/send_pds
Local SET| TEST_MODE tx_packet NB_FRAME 0
'TEST_MODE tx_packet NB_FRAME 0'
>>>
Here we see no call to wfx_test_agent when calling channel(7), since no test data is sent at this time, by design
of the Python3 code. Test data is sent when calling tx_start(...). We see the test data being sent in compressed format
using wfx_test_agent write_test_data <data_string>
Calling tx_start('continuous')
, as we can see above, sets NB_FRAME to 0, and the FW will enter permanent Tx mode.
>>> print(dut.run('dmesg | tail'))
Local D>>| dmesg | tail
<<D Local| [942575.126128] wfx_wlan: TX packet test ongoing...
<<D Local| [942576.126134] wfx_wlan: TX packet test ongoing...
<<D Local| [942577.126136] wfx_wlan: TX packet test ongoing...
<<D Local| [942578.126161] wfx_wlan: TX packet test ongoing...
<<D Local| [942579.126089] wfx_wlan: TX packet test ongoing...
<<D Local| [942580.126097] wfx_wlan: TX packet test ongoing...
<<D Local| [942581.126079] wfx_wlan: TX packet test ongoing...
<<D Local| [942582.126087] wfx_wlan: TX packet test ongoing...
<<D Local| [942583.126081] wfx_wlan: TX packet test ongoing...
<<D Local| [942584.126201] wfx_wlan: TX packet test ongoing...
[942575.126128] wfx_wlan: TX packet test ongoing...
[942576.126134] wfx_wlan: TX packet test ongoing...
[942577.126136] wfx_wlan: TX packet test ongoing...
[942578.126161] wfx_wlan: TX packet test ongoing...
[942579.126089] wfx_wlan: TX packet test ongoing...
[942580.126097] wfx_wlan: TX packet test ongoing...
[942581.126079] wfx_wlan: TX packet test ongoing...
[942582.126087] wfx_wlan: TX packet test ongoing...
[942583.126081] wfx_wlan: TX packet test ongoing...
[942584.126201] wfx_wlan: TX packet test ongoing...
>>>
The above is using a Linux-specific command to illustrate the fact that any OS command is callable using dut.run('command')
Under Linux, we see here the 'HI_GENERIC_INDICATION_TYPE_STRING' string messages received by the
driver being copied to dmesg. We can also see that they appear with a period of 1000 ms,
matching the default value of TEST_IND (set using dut.test_ind_period(period_ms)
).
NB: There is no driver processing on those messages, they are directly coming from the FW 'as is'
>>> dut.tx_stop()
Local D>>| wfx_test_agent write_test_data "{i:{a:7,b:1,f:3E8,c:{a:0,b:1,c:2,d:44},d:{a:BB8,b:0,c:0,d:15,e:64,f:4},e:{}}}"
<<D Local| '{i:{a:7,b:1,f:3E8,c:{a:0,b:1,c:2,d:44},d:{a:BB8,b:0,c:0,d:15,e:64,f:4},e:{}}}' sent to /sys/kernel/debug/ieee80211/phy0/wfx/send_pds
Local SET| TEST_MODE tx_packet NB_FRAME 100
'TEST_MODE tx_packet NB_FRAME 100'
>>>
Once we have measured the Tx signal (using our test equipment), we stop the transmission. There is no real 'stop' feature in the FW, so we use a 'Tx 100 frames then stop' setting (less than 25 frames is not accepted by the FW)
>>> dut.link.trace = False
>>> print(dut.run('dmesg | tail -n 5'))
[943517.134836] wfx_wlan: TX packet test ongoing...
[943518.134831] wfx_wlan: TX packet test ongoing...
[943518.170225] wfx_wlan: End of TX packet test
[943518.171552] wfx_wlan: Start TX packet test feature
[943518.212839] wfx_wlan: End of TX packet test
>>>
Here we stop the link trace to avoid too many lines, and we check the content of dmesg
. We see a 'Start TX' message closely followed by an 'End of TX' message. These 2 correspond to the transmission of 100 frames we triggered to stop transmitting
>>> quit()
Use quit()
to stop testing
python3
Python 3.4.2 (default, Oct 19 2014, 13:31:11)
[GCC 4.9.1] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from wfx_test_dut import *
/usr/local/lib/python3.4/dist-packages/cryptography/hazmat/bindings/openssl/binding.py:163: CryptographyDeprecationWarning: OpenSSL version 1.0.1 is no longer supported by the OpenSSL project, please upgrade. A future version of cryptography will drop support for it.
utils.CryptographyDeprecationWarning
Disregard the CryptographyDeprecationWarning above, since it's related to paramiko dependencies, and dealing with these it out of our scope (as long as it still works)
>>> dut = WfxTestDut('SSH', host='127.0.0.1', user='pi', password='default_password', port=22)
SSH: Configuring a SSH connection to host 127.0.0.1 for user pi
INFO:root:SSH I'm connected to 127.0.0.1:22 as pi
Good news: The rest is strictly identical to the Direct on Raspberry PI (without SSH) case!
python3
Python 3.4.2 (default, Oct 19 2014, 13:31:11)
[GCC 4.9.1] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from wfx_test_dut import *
>>> dut = WfxTestDut('SSH', host='10.5.124.186', user='root', port=22)
SSH: Configuring a SSH connection to host 10.5.124.186 for user root
/usr/local/lib/python3.4/dist-packages/cryptography/hazmat/bindings/openssl/binding.py:163: CryptographyDeprecationWarning: OpenSSL version 1.0.1 is no longer supported by the OpenSSL project, please upgrade. A future version of cryptography will drop support for it.
utils.CryptographyDeprecationWarning
/usr/local/lib/python3.4/dist-packages/paramiko/kex_ecdh_nist.py:39: CryptographyDeprecationWarning: encode_point has been deprecated on EllipticCurvePublicNumbers and will be removed in a future version. Please use EllipticCurvePublicKey.public_bytes to obtain both compressed and uncompressed point encoding.
m.add_string(self.Q_C.public_numbers().encode_point())
/usr/local/lib/python3.4/dist-packages/paramiko/kex_ecdh_nist.py:96:
CryptographyDeprecationWarning: Support for unsafe construction of public numbers from encoded data will be removed in a future version. Please use EllipticCurvePublicKey.from_encoded_point
self.curve, Q_S_bytes
/usr/local/lib/python3.4/dist-packages/paramiko/kex_ecdh_nist.py:111: CryptographyDeprecationWarning: encode_point has been deprecated on EllipticCurvePublicNumbers and will be removed in a future version. Please use EllipticCurvePublicKey.public_bytes to obtain both compressed and uncompressed point encoding.
hm.add_string(self.Q_C.public_numbers().encode_point())
Disregard the CryptographyDeprecationWarning above, since it's related to paramiko dependencies, and dealing with these it out of our scope (as long as it still works)
INFO:root:SSH I'm connected to 10.5.124.186:22 as root
Good news: The rest is strictly identical to the Direct on Raspberry PI (without SSH) case!
python
Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 21:26:53) [MSC v.1916 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> from wfx_test_dut import *
>>> dut = WfxTestDut('Serial', port='COM21', baudrate=115200, bytesize=8, parity='N', stopbits=1)
Serial: Configuring a UART connection using COM21
Serial U>>| wfx_test_agent
<<U Serial| Unknown command
The lines with Serial U>>|
or <<U Serial|
are write/read traces coming from the connection link.
These can be disabled using dut.link.trace = False
. They are useful to check/debug the wfx_test_agent behavior.
Above we see the result of the wfx_test_agent call with no option. It still returns a string with Unknown command
At this point, it proves there is communication with the DUT MCU
Serial U>>| wfx_test_agent read_fw_version
<<U Serial| 2.3.0
Serial: fw_version retrieved from HW (2.3.0)
Serial: tree filled for FW2.3.0
Here we see that we can retrieve the FW version from the wfx_test_agent. It is used to select which parameters
will be added to the test_data for this DUT. If the fw_version option is not supported by the wfx_test_agent, it can be set adding , fw_version='<version>'
in the call to WfxTestDut(...)
. By default, it will otherwise assume that all test data parameters are supported.
Serial U>>| wfx_test_agent read_agent_version
<<U Serial| Unknown command
UART COM21/115200/8/N/1 agent_reply: Unknown command
>>> dut.test_conditions()
Serial U>>| wfx_test_agent read_agent_version
<<U Serial| Unknown command
Serial U>>| wfx_test_agent read_fw_version
<<U Serial| 2.3.0
Serial U>>| wfx_test_agent read_driver_version
<<U Serial| Not supported
'Test conditions: DUT Serial / Driver Not supported / FW 2.3.0 / Tools 1.0.0 / Agent Unknown command / UART COM21/115200/8/N/1'
>>>
Here we see that even though some options are not implemented in the wfx_test_agent, we can retrieve useful info anyway to log our test conditions for this DUT Good news: The rest is strictly identical to the Direct on Raspberry PI (without SSH) case, except the Linux OS commands, of course. But existing RTOS OS commands can be called!
python
Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 21:26:53) [MSC v.1916 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> from wfx_test_dut import *
>>> uart_dut = WfxTestDut('Serial', port='COM21', baudrate=115200, bytesize=8, parity='N', stopbits=1)
Serial: Configuring a UART connection using COM21
Serial U>>| wfx_test_agent
<<U Serial| Unknown command
Serial U>>| wfx_test_agent read_fw_version
<<U Serial| 2.3.0
Serial: fw_version retrieved from HW (2.3.0)
Serial: tree filled for FW2.3.0
Serial U>>| wfx_test_agent read_agent_version
<<U Serial| Unknown command
UART COM21/115200/8/N/1 agent_reply: Unknown command
An uart DUT has just been configured...
>>> ssh_dut = WfxTestDut('SSH', host='10.5.124.186', user='root', port=22)
SSH: Configuring a SSH connection to host 10.5.124.186 for user root
C:\Program Files (x86)\Python37-32\lib\site-packages\paramiko\kex_ecdh_nist.py:39: CryptographyDeprecationWarning: encode_point has been deprecated on EllipticCurvePublicNumbers and will be removed in a future version. Please use EllipticCurvePublicKey.public_bytes to obtain both compressed and uncompressed point encoding.
m.add_string(self.Q_C.public_numbers().encode_point())
C:\Program Files (x86)\Python37-32\lib\site-packages\paramiko\kex_ecdh_nist.py:96: CryptographyDeprecationWarning: Support for unsafe construction of public numbers from encoded data will be removed in a future version. Please use EllipticCurvePublicKey.from_encoded_point
self.curve, Q_S_bytes
C:\Program Files (x86)\Python37-32\lib\site-packages\paramiko\kex_ecdh_nist.py:111: CryptographyDeprecationWarning: encode_point has been deprecated on EllipticCurvePublicNumbers and will be removed in a future version. Please use EllipticCurvePublicKey.public_bytes to obtain both compressed and uncompressed point encoding.
hm.add_string(self.Q_C.public_numbers().encode_point())
INFO:root:SSH I'm connected to 10.5.124.186:22 as root
SSH S>>| wfx_test_agent read_fw_version
<<S SSH| 2.2.1
SSH: fw_version retrieved from HW (2.2.1)
Info: 'FRONT_END_LOSS_RX_QDB' cannot be supported with FW2.2.1, it has been added in FW2.2.2 (skipped)
fill_tree has messages:
Info: 'FRONT_END_LOSS_RX_QDB' cannot be supported with FW2.2.1, it has been added in FW2.2.2 (skipped)
SSH: tree filled for FW2.2.1
SSH S>>| wfx_test_agent read_agent_version
<<S SSH| 1.0.0
SSH pi@10.5.124.186:22 agent_reply: 1.0.0
A SSH DUT has just been configured...
>>> uart_dut.link.trace = False
>>> ssh_dut.link.trace = False
>>> uart_dut.test_conditions()
'Test conditions: DUT Serial / Driver Not supported / FW 2.3.0 / Tools 1.0.0 / Agent Unknown command / UART COM21/115200/8/N/1'
>>> ssh_dut.test_conditions()
'Test conditions: DUT SSH / Driver 2.0.3 / FW 2.2.1 / Tools 1.0.0 / Agent 1.0.0 / SSH [email protected]:22'
>>>
We can access the uart DUT using uart_dut.<>
and the SSH DUT using ssh_dut.<>
. Many DUTs can be part of our test.
If we rely on scripts using dut.<>
, we can use dut = uart_dut
or dut = ssh_dut
to switch between DUTs
Any modification of FEM test conditions shall begin with dut.tx_stop() and end with dut.tx_start('continuous') as shown in the following open loop and closed loop tests of a (26dB typ/28dB max) FEM achieving 23dBm output power in DSSS 1Mbps
>>> dut.tx_stop()
'TEST_MODE tx_packet NB_FRAME 100'
>>> dut.tx_mode('DSSS_1')
'HT_PARAM MM RATE B_1Mbps'
>>> dut.fem_pa_used('yes')
'PA_USED yes'
>>> dut.fem_pa_table('open_loop')
'NB_OF_POINTS 0'
>>> dut.fem_pa_max_gain(26)
'MAX_GAIN 104'
>>> dut.tx_power(24)
'MAX_OUTPUT_POWER_QDBM 96 TEST_MODE tx_packet NB_FRAME 0'
>>> dut.tx_start('continuous')
'TEST_MODE tx_packet NB_FRAME 0'
>>> dut.fem_read_vpdet()
'1080'
>>> dut.tx_stop()
'TEST_MODE tx_packet NB_FRAME 100'
>>> dut.tx_power(23)
'MAX_OUTPUT_POWER_QDBM 92 TEST_MODE tx_packet NB_FRAME 0'
>>> dut.tx_start('continuous')
'TEST_MODE tx_packet NB_FRAME 0'
>>> dut.fem_read_vpdet()
'925'
Note that value of dut.fem_pa_max_gain() should be adjusted (1/4 dB resolution) to ensure that dut.tx_power(value in dBm) expected is indeed measured by Wi-Fi tester at DUT RF output.
Once table of interpolation points has been measured in open loop, FEM can be controlled in closed loop:
>>> dut.tx_stop()
'TEST_MODE tx_packet NB_FRAME 100'
>>> dut.fem_pa_used('yes')
'PA_USED yes'
>>> dut.fem_pa_table([[1080, 96], [925, 92], [818, 88], [752, 84], [682, 80], [624, 76], [570, 72], [518, 68], [478, 64], [438, 60], [377, 52], [328, 44], [289, 36], [259, 28], [234, 20], [216, 12]])
'VDET_VAL [1080,925,818,752,682,624,570,518,478,438,377,328,289,259,234,216]\nPOUT_VAL [96,92,88,84,80,76,72,68,64,60,52,44,36,28,20,12]'
>>> dut.fem_pa_table('closed_loop')
'NB_OF_POINTS 16'
>>> dut.fem_pa_max_gain(28)
'MAX_GAIN 112'
>>> dut.tx_power(24)
'MAX_OUTPUT_POWER_QDBM 96 TEST_MODE tx_packet NB_FRAME 0'
>>> dut.tx_start('continuous')
'TEST_MODE tx_packet NB_FRAME 0'
>>> dut.fem_read_vpdet()
'1094'
>>> dut.fem_read_fem_pout()
'24.00'
>>> dut.tx_stop()
'TEST_MODE tx_packet NB_FRAME 100'
>>> dut.tx_power(23)
'MAX_OUTPUT_POWER_QDBM 92 TEST_MODE tx_packet NB_FRAME 0'
>>> dut.tx_start('continuous')
'TEST_MODE tx_packet NB_FRAME 0'
>>> dut.fem_read_vpdet()
'932'
>>> dut.fem_read_fem_pout()
'23.00'
--------------------------------------------------------
|
---------------------- |
| customer_test.py | |
---------------------- |
| |
\|/ |
---------------------- |
| serverPI_UDP.py | |
---------------------- | Running
| | on
\|/ | Tester
---------------------- |
| wfx_test_dut.py | |
---------------------- |
| |
|------------------------| |
\|/ \|/ |
---------------------- ------------------------ |
| wfx_test_target.py | | job.py | |
---------------------- ------------------------ |
| |
|------------------------|---------------------------| |
\|/ \|/ \|/ |
---------------------- ------------------------ ------------------------- |
| wfx_connection.py | | pds_compress.py | | wfx_pds_tree.py | |
---------------------- ------------------------ ------------------------- |
/|\ |
| --------------------------------------------------------
|
| . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . UART or SSH connection
|
| --------------------------------------------------------
\|/ |
---------------------- | Running
| wfx_test_agent.py | | on
---------------------- | DUT(s)
|
--------------------------------------------------------
Links: