-
Notifications
You must be signed in to change notification settings - Fork 19
/
Copy pathLBAND.h
214 lines (197 loc) · 8.13 KB
/
LBAND.h
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
/*
* Copyright 2022 by Michael Ammann (@mazgch)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __LBAND_H__
#define __LBAND_H__
#include <SparkFun_u-blox_GNSS_Arduino_Library.h>
#include "GNSS.h" // required vor version, defines or macros
const int LBAND_I2C_ADR = 0x43; //!< NEO-D9S I2C address
//! because rx.softwareEnableGNSS(en) is not yet available in the sparkfun library
#define softwareEnableGNSS(en) setVal(qzss ? UBLOX_CFG_MSGOUT_UBX_RXM_QZSSL6_I2C \
: UBLOX_CFG_MSGOUT_UBX_RXM_PMP_I2C, en, VAL_LAYER_RAM)
/** This class encapsulates all LBAND functions.
*/
class LBAND {
public:
/** constructor
*/
LBAND () {
online = false;
qzss = false;
curFreq = 0;
curPower = false;
ttagNextTry = millis();
}
/** detect and configure the receiver, inject saved keys
* \return true if receiver is sucessfully detected, false if not
*/
bool detect(void) {
//rx.enableDebugging()
rx.setOutputPort(Websocket); // forward all messages
bool ok = rx.begin(UbxWire, LBAND_I2C_ADR);
if (ok) {
log_i("receiver detected");
String fwver = GNSS::version("LBAND", &rx);
qzss = fwver.startsWith("QZS");
String useSrc = Config.getValue(CONFIG_VALUE_USESOURCE);
bool useLband = (-1 != useSrc.indexOf("LBAND"));
GNSS_CHECK_INIT;
GNSS_CHECK(1) = rx.setVal32(UBLOX_CFG_UART2_BAUDRATE, 38400, VAL_LAYER_RAM);
if (qzss) { // NEO-D9C
curFreq = 0;
curPower = useLband && Config.getValue(CONFIG_VALUE_REGION).equals("jp");
rx.setRXMQZSSL6messageCallbackPtr(onRXMQZSSL6);
// prepare the UART 2
GNSS_CHECK(2) = rx.setVal(UBLOX_CFG_MSGOUT_UBX_RXM_QZSSL6_UART2, 1, VAL_LAYER_RAM);
// prepare I2C
GNSS_CHECK(3) = rx.setVal(UBLOX_CFG_MSGOUT_UBX_RXM_QZSSL6_I2C, 1, VAL_LAYER_RAM);
} else { // NEO-D9S
curFreq = Config.getFreq();
curPower = useLband && (0 < curFreq);
rx.setRXMPMPmessageCallbackPtr(onRXMPMP);
// prepare the UART 2
GNSS_CHECK(4) = rx.setVal(UBLOX_CFG_MSGOUT_UBX_RXM_PMP_UART2, 1, VAL_LAYER_RAM);
// prepare I2C
GNSS_CHECK(5) = rx.setVal(UBLOX_CFG_MSGOUT_UBX_RXM_PMP_I2C, 1, VAL_LAYER_RAM);
GNSS_CHECK(6) = rx.setVal(UBLOX_CFG_MSGOUT_UBX_MON_PMP_I2C, 1, VAL_LAYER_RAM);
// contact [email protected] to get NEO-D9S configuration parameters for PointPerfect LBAND satellite augmentation service in EU / US
// https://developer.thingstream.io/guides/location-services/pointperfect-getting-started/pointperfect-l-band-configuration
GNSS_CHECK(7) = rx.setVal8(0x10b10016, 0, VAL_LAYER_RAM);
GNSS_CHECK(8) = rx.setVal16(0x30b10015, 0x6959, VAL_LAYER_RAM);
GNSS_CHECK(9)= rx.setVal32(UBLOX_CFG_PMP_CENTER_FREQUENCY, curFreq, VAL_LAYER_RAM);
}
online = ok = GNSS_CHECK_OK;
GNSS_CHECK_EVAL("configuration");
if (ok) {
rx.softwareEnableGNSS(curPower);
if (qzss) {
log_i("configuration complete, receiver online, %s", curPower ? "started" : "stopped");
} else {
log_i("configuration complete, receiver online, freq %d %s", curFreq, curPower ? "started" : "stopped");
}
}
}
return ok;
}
/** This needs to be called from a task periodically, it makes sure the receiver is detected
* and callbacks are processed.
*/
void poll(void) {
int32_t now = millis();
if (0 >= (ttagNextTry - now)) {
ttagNextTry = now + GNSS_DETECT_RETRY;
if (!online) {
detect();
} else {
config();
}
}
if (online) {
rx.checkUblox();
rx.checkCallbacks();
}
}
protected:
/** process the UBX-RXM-PMP message, extract information for the console and inject to the GNSS
* \param pmpData the UBX-RXM-PMP payload
*/
static void onRXMPMP(UBX_RXM_PMP_message_data_t *pmpData)
{
if (NULL != pmpData) {
GNSS::MSG msg;
uint16_t size = ((uint16_t)pmpData->lengthMSB << 8) | (uint16_t)pmpData->lengthLSB;
msg.size = size + 8;
msg.data = new uint8_t[msg.size];
double ebn0 = 0.125 * pmpData->payload[22];
uint16_t serviceId = pmpData->payload[16] + ((uint16_t)pmpData->payload[17] << 8);
if (NULL != msg.data) {
msg.source = GNSS::SOURCE::LBAND;
memcpy(msg.data, &pmpData->sync1, size + 6);
memcpy(&msg.data[size + 6], &pmpData->checksumA, 2);
log_i("received RXM-PMP with %d bytes Eb/N0 %.1f dB id 0x%04X", msg.size, ebn0, serviceId);
Gnss.inject(msg); // Push the sync chars, class, ID, length and payload
} else {
log_e("received RXM-PMP with %d bytes Eb/N0 %.1f dB id 0x%04X, no memory", msg.size, ebn0, serviceId);
}
}
}
/** process the UBX-RXM-QZSSL6 message, extract information for the console and inject to the GNSS
* \param qzssData the UBX-RXM-QZSSL6 payload
*/
static void onRXMQZSSL6(UBX_RXM_QZSSL6_message_data_t *qzssData)
{
if (NULL != qzssData) {
GNSS::MSG msg;
uint16_t size = ((uint16_t)qzssData->lengthMSB << 8) | (uint16_t)qzssData->lengthLSB;
msg.size = size + 8;
msg.data = new uint8_t[msg.size];
int svid = qzssData->payload[1];
double cno = 0.00390625 * qzssData->payload[2] + qzssData->payload[3];
if (NULL != msg.data) {
msg.source = GNSS::SOURCE::LBAND;
memcpy(msg.data, &qzssData->sync1, size + 6);
memcpy(&msg.data[size + 6], &qzssData->checksumA, 2);
log_i("received RXM-QZSSL6 with %d bytes prn %d C/N0 %.1f dB", msg.size, svid, cno);
Gnss.inject(msg); // Push the sync chars, class, ID, length and payload
} else {
log_e("received RXM-QZSSL6 with %d bytes prn %d C/N0 %.1f dB, no memory", msg.size, svid, cno);
}
}
}
/** Make sure that the receiver has the right frequency configured, this depends on the region/location and
* switch power off if the region does not support the signal or have a LBAND frequency to recive
*/
void config(void) {
// do update the ferquency from time to time
String useSrc = Config.getValue(CONFIG_VALUE_USESOURCE);
bool useLband = (-1 != useSrc.indexOf("LBAND"));
bool newPower;
if (qzss) {
newPower = useLband && Config.getValue(CONFIG_VALUE_REGION).equals("jp");
} else {
int newFreq = Config.getFreq();
newPower = useLband && (0 < newFreq);
if (newPower && (curFreq != newFreq)) {
if (rx.setVal32(UBLOX_CFG_PMP_CENTER_FREQUENCY, newFreq, VAL_LAYER_RAM)) {
curFreq = newFreq;
if (curPower) {
rx.softwareResetGNSSOnly();
log_i("config freq %d, reset", newFreq);
} else {
rx.softwareEnableGNSS(true);
curPower = true;
log_i("config freq %d, started", newFreq);
}
} else {
log_w("config freq %d, failed, retry later", newFreq);
newPower = curPower; // don't change the power in that case
}
}
}
if (curPower != newPower) {
rx.softwareEnableGNSS(newPower);
curPower = newPower;
log_i("%s", newPower ? "started" : "stopped");
}
}
bool online; //!< flag that indicates if the receiver is connected
int32_t ttagNextTry; //!< time tag when to call the state machine again
SFE_UBLOX_GNSS rx; //!< the receiver object
uint32_t curFreq; //!< the current configured frequency
bool curPower; //!< the current power mode
bool qzss; //!<true if the receiver is a NEO-D9C
};
LBAND LBand; //!< The global GNSS peripherial object
#endif // __LBAND_H__