IEEE 802.15.4 subsystem
Sign in or create your account | Project List | Help
IEEE 802.15.4 subsystem Git Source Tree
Root/
Source at commit 2c1cb715e4d1cdeb0fce0d85377315525836e7bb created 12 years 11 months ago. By Werner Almesberger, atrf-path: sweep offsets separately, so that the we can reuse the cw setup | |
---|---|
1 | /* |
2 | * atrf-path/atrf-path.c - Measure path characteristics |
3 | * |
4 | * Written 2011 by Werner Almesberger |
5 | * Copyright 2011 Werner Almesberger |
6 | * |
7 | * This program is free software; you can redistribute it and/or modify |
8 | * it under the terms of the GNU General Public License as published by |
9 | * the Free Software Foundation; either version 2 of the License, or |
10 | * (at your option) any later version. |
11 | */ |
12 | |
13 | |
14 | #include <stdlib.h> |
15 | #include <stdio.h> |
16 | #include <unistd.h> |
17 | |
18 | #include "at86rf230.h" |
19 | |
20 | #include "misctxrx.h" |
21 | #include "cwtest.h" |
22 | #include "atrf.h" |
23 | |
24 | #include "gui.h" |
25 | #include "sweep.h" |
26 | |
27 | |
28 | #define DEFAULT_TRIM 8 |
29 | #define DEFAULT_POWER 15 |
30 | |
31 | |
32 | static void set_channel(struct atrf_dsc *dsc, int chan) |
33 | { |
34 | atrf_reg_write(dsc, REG_PHY_CC_CCA, (1 << CCA_MODE_SHIFT) | chan); |
35 | } |
36 | |
37 | |
38 | static void init_common(struct atrf_dsc *dsc, int trim) |
39 | { |
40 | atrf_reg_write(dsc, REG_TRX_STATE, TRX_CMD_TRX_OFF); |
41 | atrf_reg_write(dsc, REG_XOSC_CTRL, |
42 | (XTAL_MODE_INT << XTAL_MODE_SHIFT) | trim); |
43 | atrf_set_clkm(dsc, 0); |
44 | } |
45 | |
46 | |
47 | static void init_tx(struct atrf_dsc *dsc, int trim, int power) |
48 | { |
49 | init_common(dsc, trim); |
50 | set_power_step(dsc, power, 0); |
51 | } |
52 | |
53 | |
54 | static void init_rx(struct atrf_dsc *dsc, int trim) |
55 | { |
56 | init_common(dsc, trim); |
57 | atrf_reg_write(dsc, REG_TRX_STATE, TRX_CMD_RX_ON); |
58 | } |
59 | |
60 | |
61 | static double rssi_to_dBm(double rssi) |
62 | { |
63 | return -91+3*(rssi-1); |
64 | } |
65 | |
66 | |
67 | static void sample(const struct sweep *sweep, int cont_tx, |
68 | struct sample *res, int first) |
69 | { |
70 | int i, rssi; |
71 | int sum = 0, min = -1, max = -1; |
72 | double offset = tx_power_step2dBm(sweep->tx, sweep->power); |
73 | |
74 | /* |
75 | * For the 230, we don't have reset-less exit from test mode (yet ?) and |
76 | * need to set up things from scratch: |
77 | * |
78 | * init_tx(sweep->tx, sweep->trim_tx, sweep->power); |
79 | * set_channel(sweep->tx, chan); |
80 | * usleep(155); / * table 7-2, tTR19 * / |
81 | */ |
82 | if (first) |
83 | cw_test_begin(sweep->tx, cont_tx); |
84 | else |
85 | cw_test_resume(sweep->tx); |
86 | /* table 7-1, tTR10, doubling since it's a "typical" value */ |
87 | usleep(2*16); |
88 | |
89 | for (i = 0; i != sweep->samples; i++) { |
90 | /* according to 8.3.2, PHY_RSSI is updated every 2 us */ |
91 | usleep(2); |
92 | |
93 | rssi = atrf_reg_read(sweep->rx, REG_PHY_RSSI) & RSSI_MASK; |
94 | sum += rssi; |
95 | if (min == -1 || rssi < min) |
96 | min = rssi; |
97 | if (rssi > max) |
98 | max = rssi; |
99 | } |
100 | |
101 | cw_test_end(sweep->tx); |
102 | |
103 | res->avg = rssi_to_dBm((double) sum/sweep->samples)-offset; |
104 | res->min = rssi_to_dBm(min)-offset; |
105 | res->max = rssi_to_dBm(max)-offset; |
106 | } |
107 | |
108 | |
109 | void do_sweep(const struct sweep *sweep, struct sample *res) |
110 | { |
111 | struct sample *r; |
112 | int chan; |
113 | |
114 | r = res; |
115 | for (chan = 11; chan <= 26; chan++) { |
116 | set_channel(sweep->rx, chan); |
117 | set_channel(sweep->tx, chan); |
118 | usleep(155); /* table 7-2, tTR19 */ |
119 | |
120 | sample(sweep, CONT_TX_M500K, r, chan == 11); |
121 | r += 2; |
122 | } |
123 | r = res+1; |
124 | for (chan = 11; chan <= 26; chan++) { |
125 | set_channel(sweep->rx, chan); |
126 | set_channel(sweep->tx, chan); |
127 | usleep(155); /* table 7-2, tTR19 */ |
128 | |
129 | sample(sweep, CONT_TX_P500K, r, chan == 11); |
130 | r += 2; |
131 | } |
132 | } |
133 | |
134 | |
135 | static void print_sweep(const struct sweep *sweep, const struct sample *res) |
136 | { |
137 | int chan; |
138 | |
139 | for (chan = 11; chan <= 26; chan++) { |
140 | printf("%.1f %.2f %.0f %.0f\n", |
141 | 2350+5*chan-0.5, res->avg, res->min, res->max); |
142 | res++; |
143 | printf("%.1f %.2f %.0f %.0f\n", |
144 | 2350+5*chan+0.5, res->avg, res->min, res->max); |
145 | res++; |
146 | } |
147 | } |
148 | |
149 | |
150 | static void do_sweeps(const struct sweep *sweep, int sweeps) |
151 | { |
152 | struct sample res[16*2]; /* 16 channels, 2 offsets */ |
153 | int i; |
154 | |
155 | for (i = 0; i != sweeps; i++) { |
156 | if (i) |
157 | putchar('\n'); |
158 | do_sweep(sweep, res); |
159 | print_sweep(sweep, res); |
160 | } |
161 | } |
162 | |
163 | |
164 | static void usage(const char *name) |
165 | { |
166 | fprintf(stderr, |
167 | "usage: %s [-p power] [-t trim_tx [-t trim_rx]] driver_tx[:arg]\n" |
168 | "%16s driver_rx[:arg] [sweeps [samples]]\n" |
169 | |
170 | #ifdef HAVE_GFX |
171 | "%6s %s -g [-p power] [-t trim_tx [-t trim_rx]] driver_tx[:arg]\n" |
172 | "%16s driver_rx[:arg] [[sweeps] samples]\n" |
173 | #endif |
174 | "\n" |
175 | |
176 | #ifdef HAVE_GFX |
177 | " -g display results graphically\n" |
178 | #endif |
179 | " -p power transmit power, 0 to 15 (default %d)\n" |
180 | " -t trim trim capacitor, 0 to 15 (default %d)\n" |
181 | |
182 | , name, "", |
183 | #ifdef HAVE_GFX |
184 | "", name, "", |
185 | #endif |
186 | DEFAULT_POWER, DEFAULT_TRIM); |
187 | |
188 | exit(1); |
189 | } |
190 | |
191 | |
192 | int main(int argc, char **argv) |
193 | { |
194 | const char *tx_drv, *rx_drv; |
195 | struct sweep sweep = { |
196 | .trim_tx = -1, |
197 | .trim_rx = DEFAULT_TRIM, |
198 | .samples = 1, |
199 | }; |
200 | int graphical = 0; |
201 | int power = DEFAULT_POWER; |
202 | int sweeps = 1; |
203 | unsigned long tmp; |
204 | char *end; |
205 | int c; |
206 | |
207 | while ((c = getopt(argc, argv, "gp:t:")) != EOF) |
208 | switch (c) { |
209 | case'g': |
210 | graphical = 1; |
211 | sweeps = 0; |
212 | break; |
213 | case 'p': |
214 | tmp = strtoul(optarg, &end, 0); |
215 | if (*end || tmp > 15) |
216 | usage(*argv); |
217 | power = tmp; |
218 | break; |
219 | case 't': |
220 | tmp = strtoul(optarg, &end, 0); |
221 | if (*end || tmp > 15) |
222 | usage(*argv); |
223 | if (sweep.trim_tx == -1) |
224 | sweep.trim_tx = tmp; |
225 | else |
226 | sweep.trim_rx = tmp; |
227 | break; |
228 | default: |
229 | usage(*argv); |
230 | } |
231 | |
232 | if (sweep.trim_tx == -1) |
233 | sweep.trim_tx = DEFAULT_TRIM; |
234 | |
235 | switch (argc-optind) { |
236 | case 4: |
237 | sweep.samples = strtoul(argv[optind+3], &end, 0); |
238 | if (*end) |
239 | usage(*argv); |
240 | /* fall through */ |
241 | case 3: |
242 | sweeps = strtoul(argv[optind+2], &end, 0); |
243 | if (*end) |
244 | usage(*argv); |
245 | if (graphical && argc-optind == 3) { |
246 | sweep.samples = sweeps; |
247 | sweeps = 0; |
248 | } |
249 | /* fall through */ |
250 | case 2: |
251 | tx_drv = argv[optind]; |
252 | rx_drv = argv[optind+1]; |
253 | break; |
254 | default: |
255 | usage(*argv); |
256 | } |
257 | |
258 | sweep.tx = atrf_open(tx_drv); |
259 | if (!sweep.tx) |
260 | return 1; |
261 | sweep.rx = atrf_open(rx_drv); |
262 | if (!sweep.rx) |
263 | return 1; |
264 | |
265 | sweep.power = 15-power; |
266 | init_rx(sweep.rx, sweep.trim_rx); |
267 | init_tx(sweep.tx, sweep.trim_tx, sweep.power); |
268 | if (graphical) |
269 | gui(&sweep, sweeps); |
270 | else |
271 | do_sweeps(&sweep, sweeps); |
272 | |
273 | atrf_reg_write(sweep.tx, REG_TRX_STATE, TRX_CMD_TRX_OFF); |
274 | atrf_reg_write(sweep.rx, REG_TRX_STATE, TRX_CMD_TRX_OFF); |
275 | |
276 | atrf_close(sweep.tx); |
277 | atrf_close(sweep.rx); |
278 | |
279 | return 0; |
280 | } |
281 |