forked from meshtastic/firmware
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathRangeTestModule.cpp
More file actions
353 lines (296 loc) · 13.1 KB
/
Copy pathRangeTestModule.cpp
File metadata and controls
353 lines (296 loc) · 13.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
/**
* @file RangeTestModule.cpp
* @brief Implementation of the RangeTestModule class and RangeTestModuleRadio class.
*
* As a sender, this module sends packets every n seconds with an incremented PacketID.
* As a receiver, this module receives packets from multiple senders and saves them to the Filesystem.
*
* The RangeTestModule class is an OSThread that runs the module.
* The RangeTestModuleRadio class handles sending and receiving packets.
*/
#include "RangeTestModule.h"
#include "FSCommon.h"
#include "MeshService.h"
#include "NodeDB.h"
#include "PowerFSM.h"
#include "RTC.h"
#include "Router.h"
#include "SPILock.h"
#include "airtime.h"
#include "configuration.h"
#include "gps/GeoCoord.h"
#include <Arduino.h>
#include <Throttle.h>
RangeTestModule *rangeTestModule;
RangeTestModuleRadio *rangeTestModuleRadio;
RangeTestModule::RangeTestModule() : concurrency::OSThread("RangeTest") {}
uint32_t packetSequence = 0;
int32_t RangeTestModule::runOnce()
{
#if defined(ARCH_ESP32) || defined(ARCH_NRF52) || defined(ARCH_STM32WL) || defined(ARCH_PORTDUINO)
/*
Uncomment the preferences below if you want to use the module
without having to configure it from the PythonAPI or WebUI.
*/
// moduleConfig.range_test.enabled = 1;
// moduleConfig.range_test.sender = 30;
// moduleConfig.range_test.save = 1;
// moduleConfig.range_test.clear_on_reboot = 1;
// Fixed position is useful when testing indoors.
// config.position.fixed_position = 1;
uint32_t senderHeartbeat = moduleConfig.range_test.sender * 1000;
if (moduleConfig.range_test.enabled) {
if (firstTime) {
rangeTestModuleRadio = new RangeTestModuleRadio();
firstTime = 0;
if (moduleConfig.range_test.clear_on_reboot) {
// User wants to delete previous range test(s)
LOG_INFO("Range Test Module - Clearing out previous test file");
rangeTestModuleRadio->removeFile();
}
if (moduleConfig.range_test.sender) {
LOG_INFO("Init Range Test Module -- Sender");
started = millis(); // make a note of when we started
return (5000); // Sending first message 5 seconds after initialization.
} else {
LOG_INFO("Init Range Test Module -- Receiver");
return disable();
// This thread does not need to run as a receiver
}
} else {
if (moduleConfig.range_test.sender) {
// If sender
LOG_INFO("Range Test Module - Sending heartbeat every %d ms", (senderHeartbeat));
LOG_INFO("gpsStatus->getLatitude() %d", gpsStatus->getLatitude());
LOG_INFO("gpsStatus->getLongitude() %d", gpsStatus->getLongitude());
LOG_INFO("gpsStatus->getHasLock() %d", gpsStatus->getHasLock());
LOG_INFO("gpsStatus->getDOP() %d", gpsStatus->getDOP());
LOG_INFO("fixed_position() %d", config.position.fixed_position);
// Only send packets if the channel is less than 25% utilized.
if (airTime->isTxAllowedChannelUtil(true)) {
rangeTestModuleRadio->sendPayload();
}
// If we have been running for more than 8 hours, turn module back off
if (!Throttle::isWithinTimespanMs(started, 28800000)) {
LOG_INFO("Range Test Module - Disable after 8 hours");
return disable();
} else {
return (senderHeartbeat);
}
} else {
return disable();
// This thread does not need to run as a receiver
}
}
} else {
LOG_INFO("Range Test Module - Disabled");
}
#endif
return disable();
}
/**
* Sends a payload to a specified destination node.
*
* @param dest The destination node number.
* @param wantReplies Whether or not to request replies from the destination node.
*/
void RangeTestModuleRadio::sendPayload(NodeNum dest, bool wantReplies)
{
meshtastic_MeshPacket *p = allocDataPacket();
p->to = dest;
p->decoded.want_response = wantReplies;
p->hop_limit = 0;
p->want_ack = false;
packetSequence++;
static char heartbeatString[MAX_LORA_PAYLOAD_LEN + 1];
snprintf(heartbeatString, sizeof(heartbeatString), "seq %u", packetSequence);
p->decoded.payload.size = strlen(heartbeatString); // You must specify how many bytes are in the reply
memcpy(p->decoded.payload.bytes, heartbeatString, p->decoded.payload.size);
service->sendToMesh(p);
// TODO: Handle this better. We want to keep the phone awake otherwise it stops sending.
powerFSM.trigger(EVENT_CONTACT_FROM_PHONE);
}
ProcessMessage RangeTestModuleRadio::handleReceived(const meshtastic_MeshPacket &mp)
{
#if defined(ARCH_ESP32) || defined(ARCH_NRF52) || defined(ARCH_STM32WL) || defined(ARCH_PORTDUINO)
if (moduleConfig.range_test.enabled) {
/*
auto &p = mp.decoded;
LOG_DEBUG("Received text msg self=0x%08x, from=0x%08x, to=0x%08x, id=%d, msg=%.*s",
LOG_INFO.getNodeNum(), mp.from, mp.to, mp.id, p.payload.size, p.payload.bytes);
*/
if (!isFromUs(&mp)) {
if (moduleConfig.range_test.save) {
appendFile(mp);
}
/*
NodeInfoLite *n = nodeDB->getMeshNode(getFrom(&mp));
LOG_DEBUG("-----------------------------------------");
LOG_DEBUG("p.payload.bytes \"%s\"", p.payload.bytes);
LOG_DEBUG("p.payload.size %d", p.payload.size);
LOG_DEBUG("---- Received Packet:");
LOG_DEBUG("mp.from %d", mp.from);
LOG_DEBUG("mp.rx_snr %f", mp.rx_snr);
LOG_DEBUG("mp.rx_rssi %f", mp.rx_rssi);
LOG_DEBUG("mp.hop_limit %d", mp.hop_limit);
LOG_DEBUG("---- Node Information of Received Packet (mp.from):");
LOG_DEBUG("n->user.long_name %s", n->user.long_name);
LOG_DEBUG("n->user.short_name %s", n->user.short_name);
LOG_DEBUG("n->has_position %d", n->has_position);
LOG_DEBUG("n->position.latitude_i %d", n->position.latitude_i);
LOG_DEBUG("n->position.longitude_i %d", n->position.longitude_i);
LOG_DEBUG("---- Current device location information:");
LOG_DEBUG("gpsStatus->getLatitude() %d", gpsStatus->getLatitude());
LOG_DEBUG("gpsStatus->getLongitude() %d", gpsStatus->getLongitude());
LOG_DEBUG("gpsStatus->getHasLock() %d", gpsStatus->getHasLock());
LOG_DEBUG("gpsStatus->getDOP() %d", gpsStatus->getDOP());
LOG_DEBUG("-----------------------------------------");
*/
}
} else {
LOG_INFO("Range Test Module Disabled");
}
#endif
return ProcessMessage::CONTINUE; // Let others look at this message also if they want
}
bool RangeTestModuleRadio::appendFile(const meshtastic_MeshPacket &mp)
{
#ifdef ARCH_ESP32
auto &p = mp.decoded;
meshtastic_NodeInfoLite *n = nodeDB->getMeshNode(getFrom(&mp));
/*
LOG_DEBUG("-----------------------------------------");
LOG_DEBUG("p.payload.bytes \"%s\"", p.payload.bytes);
LOG_DEBUG("p.payload.size %d", p.payload.size);
LOG_DEBUG("---- Received Packet:");
LOG_DEBUG("mp.from %d", mp.from);
LOG_DEBUG("mp.rx_snr %f", mp.rx_snr);
LOG_DEBUG("mp.hop_limit %d", mp.hop_limit);
LOG_DEBUG("---- Node Information of Received Packet (mp.from):");
LOG_DEBUG("n->user.long_name %s", n->user.long_name);
LOG_DEBUG("n->user.short_name %s", n->user.short_name);
LOG_DEBUG("n->has_position %d", n->has_position);
LOG_DEBUG("n->position.latitude_i %d", n->position.latitude_i);
LOG_DEBUG("n->position.longitude_i %d", n->position.longitude_i);
LOG_DEBUG("---- Current device location information:");
LOG_DEBUG("gpsStatus->getLatitude() %d", gpsStatus->getLatitude());
LOG_DEBUG("gpsStatus->getLongitude() %d", gpsStatus->getLongitude());
LOG_DEBUG("gpsStatus->getHasLock() %d", gpsStatus->getHasLock());
LOG_DEBUG("gpsStatus->getDOP() %d", gpsStatus->getDOP());
LOG_DEBUG("-----------------------------------------");
*/
concurrency::LockGuard g(spiLock);
if (!FSBegin()) {
LOG_DEBUG("An Error has occurred while mounting the filesystem");
return 0;
}
if (FSCom.totalBytes() - FSCom.usedBytes() < 51200) {
LOG_DEBUG("Filesystem doesn't have enough free space. Aborting write");
return 0;
}
FSCom.mkdir("/static");
// If the file doesn't exist, write the header.
if (!FSCom.exists("/static/rangetest.csv")) {
//--------- Write to file
File fileToWrite = FSCom.open("/static/rangetest.csv", FILE_WRITE);
if (!fileToWrite) {
LOG_ERROR("There was an error opening the file for writing");
return 0;
}
// Print the CSV header
if (fileToWrite.println("time,from,sender name,sender lat,sender long,rx lat,rx long,rx elevation,rx "
"snr,distance,hop limit,payload,rx rssi")) {
LOG_INFO("File was written");
} else {
LOG_ERROR("File write failed");
}
fileToWrite.flush();
fileToWrite.close();
}
//--------- Append content to file
File fileToAppend = FSCom.open("/static/rangetest.csv", FILE_APPEND);
if (!fileToAppend) {
LOG_ERROR("There was an error opening the file for appending");
return 0;
}
struct timeval tv;
if (!gettimeofday(&tv, NULL)) {
long hms = tv.tv_sec % SEC_PER_DAY;
hms = (hms + SEC_PER_DAY) % SEC_PER_DAY;
// Tear apart hms into h:m:s
int hour = hms / SEC_PER_HOUR;
int min = (hms % SEC_PER_HOUR) / SEC_PER_MIN;
int sec = (hms % SEC_PER_HOUR) % SEC_PER_MIN; // or hms % SEC_PER_MIN
fileToAppend.printf("%02d:%02d:%02d,", hour, min, sec); // Time
} else {
fileToAppend.printf("??:??:??,"); // Time
}
fileToAppend.printf("%d,", getFrom(&mp)); // From
fileToAppend.printf("%s,", n ? n->long_name : ""); // Long Name
meshtastic_PositionLite senderPos;
const bool haveSenderPos = nodeDB->copyNodePosition(getFrom(&mp), senderPos);
if (haveSenderPos) {
fileToAppend.printf("%f,", senderPos.latitude_i * 1e-7); // Sender Lat
fileToAppend.printf("%f,", senderPos.longitude_i * 1e-7); // Sender Long
} else {
fileToAppend.printf("0.0,0.0,");
}
if (gpsStatus->getIsConnected() || config.position.fixed_position) {
fileToAppend.printf("%f,", gpsStatus->getLatitude() * 1e-7); // RX Lat
fileToAppend.printf("%f,", gpsStatus->getLongitude() * 1e-7); // RX Long
fileToAppend.printf("%d,", gpsStatus->getAltitude()); // RX Altitude
} else {
// When the phone API is in use, the node info will be updated with position
meshtastic_PositionLite usPos;
if (nodeDB->copyNodePosition(nodeDB->getNodeNum(), usPos)) {
fileToAppend.printf("%f,", usPos.latitude_i * 1e-7); // RX Lat
fileToAppend.printf("%f,", usPos.longitude_i * 1e-7); // RX Long
fileToAppend.printf("%d,", usPos.altitude); // RX Altitude
} else {
fileToAppend.printf("0.0,0.0,0,");
}
}
fileToAppend.printf("%f,", mp.rx_snr); // RX SNR
if (haveSenderPos && senderPos.latitude_i && senderPos.longitude_i && gpsStatus->getLatitude() && gpsStatus->getLongitude()) {
float distance = GeoCoord::latLongToMeter(senderPos.latitude_i * 1e-7, senderPos.longitude_i * 1e-7,
gpsStatus->getLatitude() * 1e-7, gpsStatus->getLongitude() * 1e-7);
fileToAppend.printf("%f,", distance); // Distance in meters
} else {
fileToAppend.printf("0,");
}
fileToAppend.printf("%d,", mp.hop_limit); // Packet Hop Limit
// TODO: If quotes are found in the payload, it has to be escaped.
fileToAppend.printf("\"%s\"\n", p.payload.bytes);
fileToAppend.printf("%i,", mp.rx_rssi); // RX RSSI
fileToAppend.flush();
fileToAppend.close();
return 1;
#else
LOG_ERROR("Failed to store range test results - feature only available for ESP32");
return 0;
#endif
}
bool RangeTestModuleRadio::removeFile()
{
#ifdef ARCH_ESP32
if (!FSBegin()) {
LOG_DEBUG("An Error has occurred while mounting the filesystem");
return 0;
}
if (!FSCom.exists("/static/rangetest.csv")) {
LOG_DEBUG("No range tests found.");
return 0;
}
LOG_INFO("Deleting previous range test.");
bool result = FSCom.remove("/static/rangetest.csv");
if (!result) {
LOG_ERROR("Failed to delete range test.");
return 0;
}
LOG_INFO("Range test removed.");
return 1;
#else
LOG_ERROR("Failed to remove range test results - feature only available for ESP32");
return 0;
#endif
}