Skip to content

Commit aef6588

Browse files
martinjaegercarlescufi
authored andcommitted
tests: subsys: lorawan: add clock_sync test
Checks all commands of the application-layer clock sync service in CI. Signed-off-by: Martin Jäger <[email protected]>
1 parent 52da66a commit aef6588

File tree

6 files changed

+296
-0
lines changed

6 files changed

+296
-0
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
3+
cmake_minimum_required(VERSION 3.20.0)
4+
5+
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
6+
project(lorawan_clock_sync_test)
7+
8+
FILE(GLOB app_sources src/*.c)
9+
target_sources(app PRIVATE ${app_sources})
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
3+
# Turn off log messages for failed communication with non-existing LoRa PHY
4+
CONFIG_LORA_LOG_LEVEL_OFF=y
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright (c) 2024 A Labs GmbH
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*
6+
* This overlay defines a fake LoRa PHY node which is required to build the driver.
7+
*/
8+
9+
#include <zephyr/dt-bindings/lora/sx126x.h>
10+
11+
/ {
12+
chosen {
13+
zephyr,code-partition = &slot0_partition;
14+
};
15+
16+
aliases {
17+
lora0 = &lora;
18+
};
19+
20+
test {
21+
#address-cells = <1>;
22+
#size-cells = <1>;
23+
24+
test_spi: spi@33334444 {
25+
#address-cells = <1>;
26+
#size-cells = <0>;
27+
compatible = "vnd,spi";
28+
reg = <0x33334444 0x1000>;
29+
status = "okay";
30+
clock-frequency = <2000000>;
31+
32+
cs-gpios = <&gpio0 1 GPIO_ACTIVE_LOW>;
33+
34+
lora: lora@0 {
35+
compatible = "semtech,sx1262";
36+
status = "okay";
37+
reg = <0>;
38+
reset-gpios = <&gpio0 2 GPIO_ACTIVE_LOW>;
39+
busy-gpios = <&gpio0 3 GPIO_ACTIVE_HIGH>;
40+
tx-enable-gpios = <&gpio0 4 GPIO_ACTIVE_LOW>;
41+
rx-enable-gpios = <&gpio0 5 GPIO_ACTIVE_LOW>;
42+
dio1-gpios = <&gpio0 5 GPIO_ACTIVE_HIGH>;
43+
dio2-tx-enable;
44+
dio3-tcxo-voltage = <SX126X_DIO3_TCXO_3V3>;
45+
tcxo-power-startup-delay-ms = <5>;
46+
spi-max-frequency = <1000000>;
47+
};
48+
};
49+
};
50+
};
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
3+
CONFIG_ZTEST=y
4+
5+
# General Zephyr settings
6+
CONFIG_MAIN_STACK_SIZE=2048
7+
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048
8+
CONFIG_THREAD_NAME=y
9+
CONFIG_LOG=y
10+
11+
# LoRa PHY and required peripherals
12+
CONFIG_LORA=y
13+
CONFIG_SPI=y
14+
CONFIG_GPIO=y
15+
16+
# Random number generator required for several LoRaWAN services
17+
CONFIG_ENTROPY_GENERATOR=y
18+
19+
# LoRaWAN application layer
20+
CONFIG_LORAWAN=y
21+
CONFIG_LORAWAN_EMUL=y
22+
CONFIG_LORAMAC_REGION_EU868=y
23+
24+
# LoRaWAN services required for this test
25+
CONFIG_LORAWAN_SERVICES=y
26+
CONFIG_LORAWAN_APP_CLOCK_SYNC=y
27+
# use shortest possible periodicity for testing
28+
CONFIG_LORAWAN_APP_CLOCK_SYNC_PERIODICITY=128
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
/*
2+
* Copyright (c) 2024 A Labs GmbH
3+
* Copyright (c) 2024 tado GmbH
4+
*
5+
* SPDX-License-Identifier: Apache-2.0
6+
*/
7+
8+
#include <zephyr/device.h>
9+
#include <zephyr/kernel.h>
10+
#include <zephyr/lorawan/emul.h>
11+
#include <zephyr/lorawan/lorawan.h>
12+
#include <zephyr/sys/byteorder.h>
13+
#include <zephyr/sys/util.h>
14+
#include <zephyr/ztest.h>
15+
16+
#define CMD_PACKAGE_VERSION (0x00)
17+
#define CMD_APP_TIME (0x01)
18+
#define CMD_DEVICE_APP_TIME_PERIODICITY (0x02)
19+
#define CMD_FORCE_DEVICE_RESYNC (0x03)
20+
21+
#define CLOCK_SYNC_PORT (202)
22+
#define CLOCK_SYNC_ID
23+
24+
struct lorawan_msg {
25+
/* large enough buffer to fit maximum clock sync message length */
26+
uint8_t data[6];
27+
uint8_t len;
28+
};
29+
30+
K_MSGQ_DEFINE(uplink_msgq, sizeof(struct lorawan_msg), 10, 4);
31+
32+
void uplink_handler(uint8_t port, uint8_t len, const uint8_t *data)
33+
{
34+
struct lorawan_msg msg;
35+
int ret;
36+
37+
zassert_equal(port, CLOCK_SYNC_PORT);
38+
39+
zassert_true(len <= sizeof(msg.data));
40+
memcpy(msg.data, data, len);
41+
msg.len = len;
42+
43+
ret = k_msgq_put(&uplink_msgq, &msg, K_NO_WAIT);
44+
zassert_equal(ret, 0);
45+
}
46+
47+
ZTEST(clock_sync, test_package_version)
48+
{
49+
struct lorawan_msg ans;
50+
uint8_t req_data[] = {CMD_PACKAGE_VERSION};
51+
int ret;
52+
53+
k_msgq_purge(&uplink_msgq);
54+
55+
lorawan_emul_send_downlink(CLOCK_SYNC_PORT, false, 0, 0, sizeof(req_data), req_data);
56+
57+
ret = k_msgq_get(&uplink_msgq, &ans, K_MSEC(100));
58+
zassert_equal(ret, 0, "receiving PackageVersionAns timed out");
59+
zassert_equal(ans.len, 3);
60+
zassert_equal(ans.data[0], CMD_PACKAGE_VERSION);
61+
zassert_equal(ans.data[1], 1); /* PackageIdentifier */
62+
zassert_equal(ans.data[2], 2); /* PackageVersion */
63+
}
64+
65+
ZTEST(clock_sync, test_app_time)
66+
{
67+
uint8_t ans_data[6] = {CMD_APP_TIME};
68+
struct lorawan_msg req;
69+
uint32_t device_time;
70+
uint32_t gps_time;
71+
uint8_t token_req;
72+
int ret;
73+
74+
k_msgq_purge(&uplink_msgq);
75+
76+
/* wait for more than the default (=minimum) periodicity of 128s + 30s jitter */
77+
ret = k_msgq_get(&uplink_msgq, &req, K_SECONDS(128 + 30 + 1));
78+
zassert_equal(ret, 0, "receiving AppTimeReq timed out");
79+
zassert_equal(req.len, 6);
80+
zassert_equal(req.data[0], CMD_APP_TIME);
81+
82+
device_time = sys_get_le32(req.data + 1);
83+
token_req = req.data[5] & 0xF;
84+
zassert_within((int)device_time, (int)(k_uptime_get() / 1000), 1);
85+
86+
/* apply a time correction of 1000 seconds */
87+
sys_put_le32(1000, ans_data + 1);
88+
ans_data[5] = token_req;
89+
90+
lorawan_emul_send_downlink(CLOCK_SYNC_PORT, false, 0, 0, sizeof(ans_data), ans_data);
91+
92+
lorawan_clock_sync_get(&gps_time);
93+
zassert_within(gps_time, (k_uptime_get() / 1000) + 1000, 1);
94+
}
95+
96+
ZTEST(clock_sync, test_device_app_time_periodicity)
97+
{
98+
const uint8_t period = 1; /* actual periodicity in seconds: 128 * 2^period */
99+
uint8_t req_data[] = {
100+
CMD_DEVICE_APP_TIME_PERIODICITY,
101+
period & 0xF,
102+
};
103+
struct lorawan_msg app_time_req;
104+
struct lorawan_msg ans;
105+
uint32_t device_time;
106+
uint32_t gps_time;
107+
int ret;
108+
109+
k_msgq_purge(&uplink_msgq);
110+
111+
lorawan_emul_send_downlink(CLOCK_SYNC_PORT, false, 0, 0, sizeof(req_data), req_data);
112+
113+
ret = k_msgq_get(&uplink_msgq, &ans, K_MSEC(100));
114+
zassert_equal(ret, 0, "receiving DeviceAppTimePeriodicityAns timed out");
115+
zassert_equal(ans.len, 6);
116+
zassert_equal(ans.data[0], CMD_DEVICE_APP_TIME_PERIODICITY);
117+
zassert_equal(ans.data[1], 0);
118+
119+
device_time = sys_get_le32(ans.data + 2);
120+
lorawan_clock_sync_get(&gps_time);
121+
zassert_within(device_time, gps_time, 1);
122+
123+
/* wait for more than the old periodicity of 128s + 30s jitter */
124+
ret = k_msgq_get(&uplink_msgq, &app_time_req, K_SECONDS(128 + 30 + 1));
125+
zassert_equal(ret, -EAGAIN, "received AppTimeReq too early");
126+
127+
/* wait for another 128s to cover the new periodicity of 256s + 30s jitter */
128+
ret = k_msgq_get(&uplink_msgq, &app_time_req, K_SECONDS(128));
129+
zassert_equal(ret, 0, "receiving AppTimeReq timed out");
130+
zassert_equal(app_time_req.len, 6);
131+
zassert_equal(app_time_req.data[0], CMD_APP_TIME);
132+
133+
/* reset to minimum periodicity */
134+
req_data[1] = 0;
135+
lorawan_emul_send_downlink(CLOCK_SYNC_PORT, false, 0, 0, sizeof(req_data), req_data);
136+
ret = k_msgq_get(&uplink_msgq, &ans, K_MSEC(100));
137+
zassert_equal(ret, 0, "receiving DeviceAppTimePeriodicityAns timed out");
138+
zassert_equal(ans.len, 6);
139+
zassert_equal(ans.data[0], CMD_DEVICE_APP_TIME_PERIODICITY);
140+
}
141+
142+
ZTEST(clock_sync, test_force_device_resync)
143+
{
144+
const uint8_t nb_transmissions = 2;
145+
uint8_t resync_req_data[] = {
146+
CMD_FORCE_DEVICE_RESYNC,
147+
nb_transmissions,
148+
};
149+
struct lorawan_msg app_time_req;
150+
int ret;
151+
152+
k_msgq_purge(&uplink_msgq);
153+
154+
lorawan_emul_send_downlink(CLOCK_SYNC_PORT, false, 0, 0, sizeof(resync_req_data),
155+
resync_req_data);
156+
157+
for (int i = 0; i < nb_transmissions; i++) {
158+
/* wait for more than CLOCK_RESYNC_DELAY of 10 secs */
159+
ret = k_msgq_get(&uplink_msgq, &app_time_req, K_SECONDS(11));
160+
zassert_equal(ret, 0, "receiving AppTimeReq #%d timed out", i + 1);
161+
zassert_equal(app_time_req.len, 6);
162+
zassert_equal(app_time_req.data[0], CMD_APP_TIME);
163+
}
164+
}
165+
166+
static void *clock_sync_setup(void)
167+
{
168+
const struct device *lora_dev = DEVICE_DT_GET(DT_ALIAS(lora0));
169+
struct lorawan_join_config join_cfg = {0};
170+
int ret;
171+
172+
zassert_true(device_is_ready(lora_dev), "LoRa device not ready");
173+
174+
ret = lorawan_start();
175+
zassert_equal(ret, 0, "lorawan_start failed: %d", ret);
176+
177+
ret = lorawan_join(&join_cfg);
178+
zassert_equal(ret, 0, "lorawan_join failed: %d", ret);
179+
180+
lorawan_emul_register_uplink_callback(uplink_handler);
181+
182+
ret = lorawan_clock_sync_run();
183+
zassert_equal(ret, 0, "clock_sync_run failed: %d", ret);
184+
185+
/* wait for first messages to be processed in the background */
186+
k_sleep(K_SECONDS(1));
187+
188+
return NULL;
189+
}
190+
191+
ZTEST_SUITE(clock_sync, NULL, clock_sync_setup, NULL, NULL, NULL);
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
common:
2+
tags:
3+
- lorawan
4+
tests:
5+
lorawan.clock_sync.sim:
6+
integration_platforms:
7+
- native_sim
8+
platform_allow:
9+
- native_sim
10+
lorawan.clock_sync.phy:
11+
depends_on: lora
12+
filter: CONFIG_ENTROPY_HAS_DRIVER
13+
integration_platforms:
14+
- nucleo_wl55jc

0 commit comments

Comments
 (0)