Renderer, ...: use PixelRect::GetCenter()
[xcsoar.git] / src / Device / Driver / EWMicroRecorder.cpp
blob068f9ec1d178c8b679f61083d6056a0e06259878
1 /*
2 Copyright_License {
4 XCSoar Glide Computer - http://www.xcsoar.org/
5 Copyright (C) 2000-2013 The XCSoar Project
6 A detailed list of copyright holders can be found in the file "AUTHORS".
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; either version 2
11 of the License, or (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25 // ToDo
27 // adding baro alt sentance parser to support baro source priority if (d == pDevPrimaryBaroSource){...}
29 #include "Device/Driver/EWMicroRecorder.hpp"
30 #include "Device/Driver.hpp"
31 #include "Device/Port/Port.hpp"
32 #include "Device/Declaration.hpp"
33 #include "NMEA/Info.hpp"
34 #include "NMEA/InputLine.hpp"
35 #include "NMEA/Checksum.hpp"
36 #include "Waypoint/Waypoint.hpp"
37 #include "Units/System.hpp"
38 #include "Time/TimeoutClock.hpp"
39 #include "Operation/Operation.hpp"
40 #include "Util/StaticString.hpp"
42 #include <assert.h>
43 #include <stdio.h>
45 // Additional sentance for EW support
47 class EWMicroRecorderDevice : public AbstractDevice {
48 protected:
49 Port &port;
51 public:
52 EWMicroRecorderDevice(Port &_port)
53 :port(_port) {}
55 public:
56 virtual bool ParseNMEA(const char *line, struct NMEAInfo &info) override;
57 virtual bool Declare(const Declaration &declaration, const Waypoint *home,
58 OperationEnvironment &env) override;
61 static bool
62 ReadAltitude(NMEAInputLine &line, fixed &value_r)
64 fixed value;
65 bool available = line.ReadChecked(value);
66 char unit = line.ReadFirstChar();
67 if (!available)
68 return false;
70 if (unit == _T('f') || unit == _T('F'))
71 value = Units::ToSysUnit(value, Unit::FEET);
73 value_r = value;
74 return true;
77 bool
78 EWMicroRecorderDevice::ParseNMEA(const char *String, NMEAInfo &info)
80 if (!VerifyNMEAChecksum(String))
81 return false;
83 NMEAInputLine line(String);
84 char type[16];
85 line.Read(type, 16);
87 if (StringIsEqual(type, "$PGRMZ")) {
88 fixed value;
90 /* The normal Garmin $PGRMZ line contains the "true" barometric
91 altitude above MSL (corrected with QNH), but EWMicroRecorder
92 differs here slightly: it emits the uncorrected barometric
93 altitude. That is the only reason why we catch this sentence
94 in the driver instead of letting the generic class NMEAParser
95 do it. */
96 if (ReadAltitude(line, value))
97 info.ProvidePressureAltitude(value);
99 return true;
100 } else
101 return false;
104 static bool
105 TryConnect(Port &port, char *user_data, size_t max_user_data,
106 OperationEnvironment &env)
108 port.Flush();
109 port.Write('\x02'); // send IO Mode command
111 unsigned user_size = 0;
113 TimeoutClock timeout(8000);
115 while (true) {
116 int remaining = timeout.GetRemainingSigned();
117 if (remaining < 0)
118 return false;
120 if (port.WaitRead(env, remaining) != Port::WaitResult::READY)
121 return false;
123 int nbytes = port.Read(user_data + user_size, max_user_data - user_size);
124 if (nbytes < 0)
125 return false;
127 if (user_size == 0) {
128 const char *minus = (const char *)memchr(user_data, '-', nbytes);
129 if (minus == NULL)
130 continue;
132 user_size = user_data + nbytes - minus;
133 memmove(user_data, minus, user_size);
134 } else
135 user_size += nbytes;
137 char *end = (char *)memchr(user_data, '\x13', user_size);
138 if (end != NULL) {
139 *end = 0;
140 port.Write('\x16');
141 return true;
144 if (user_size >= max_user_data)
145 /* response too large */
146 return false;
149 return false;
152 static bool
153 TryConnectRetry(Port &port, char *user_data, size_t max_user_data,
154 OperationEnvironment &env)
156 int retries=10;
158 while (--retries)
159 if (TryConnect(port, user_data, max_user_data, env))
160 return true;
162 return false;
166 * "It is important that only alpha numeric characters are included in
167 * the declaration, as other characters such as a comma will prevent
168 * the resultant .IGC file from being validated."
170 * @see http://www.ewavionics.com/products/microRecorder/microRecorder-instructionsfull.pdf
172 static bool
173 IsValidEWChar(char ch)
175 return ch == '\r' || ch == '\n' ||
176 ch == ' ' || ch == '-' ||
177 (ch >= 'a' && ch <= 'z') ||
178 (ch >= 'A' && ch <= 'Z') ||
179 (ch >= '0' && ch <= '9');
183 * Replace all invalid characters according to IsValidEWChar() with a
184 * space.
186 static void
187 CleanString(char *p)
189 for (; *p != 0; ++p)
190 if (!IsValidEWChar(*p))
191 *p = ' ';
195 * Clean a string and write it to the Port.
197 static bool
198 WriteCleanString(Port &port, const TCHAR *p,
199 OperationEnvironment &env, unsigned timeout_ms)
201 NarrowString<256> buffer;
202 buffer.SetASCII(p);
204 CleanString(buffer.buffer());
206 return port.FullWriteString(buffer, env, timeout_ms);
209 static bool
210 WriteLabel(Port &port, const char *name, OperationEnvironment &env)
212 return port.FullWriteString(name, env, 1000) &&
213 port.FullWrite(": ", 2, env, 500);
217 * Write a name/value pair to the EW microRecorder.
219 static bool
220 WritePair(Port &port, const char *name, const TCHAR *value,
221 OperationEnvironment &env)
223 return WriteLabel(port, name, env) &&
224 WriteCleanString(port, value, env, 1000) &&
225 port.FullWrite("\r\n", 2, env, 500);
228 static bool
229 WriteGeoPoint(Port &port, const GeoPoint &value, OperationEnvironment &env)
231 int DegLat, DegLon;
232 double tmp, MinLat, MinLon;
233 TCHAR NoS, EoW;
235 // prepare latitude
236 tmp = (double)value.latitude.Degrees();
237 NoS = _T('N');
238 if (tmp < 0)
240 NoS = _T('S');
241 tmp = -tmp;
244 DegLat = (int)tmp;
245 MinLat = (tmp - DegLat) * 60 * 1000;
247 // prepare long
248 tmp = (double)value.longitude.Degrees();
249 EoW = _T('E');
250 if (tmp < 0)
252 EoW = _T('W');
253 tmp = -tmp;
256 DegLon = (int)tmp;
257 MinLon = (tmp - DegLon) * 60 * 1000;
259 char buffer[64];
260 sprintf(buffer, "%02d%05d%c%03d%05d%c",
261 DegLat, (int)MinLat, NoS,
262 DegLon, (int)MinLon, EoW);
264 return port.FullWriteString(buffer, env, 1000);
267 static bool
268 EWMicroRecorderWriteWaypoint(Port &port, const char *type,
269 const Waypoint &way_point,
270 OperationEnvironment &env)
272 return WriteLabel(port, type, env) &&
273 WriteGeoPoint(port, way_point.location, env) &&
274 port.Write(' ') &&
275 WriteCleanString(port, way_point.name.c_str(), env, 1000) &&
276 port.FullWrite("\r\n", 2, env, 500);
279 static bool
280 DeclareInner(Port &port, const Declaration &declaration,
281 OperationEnvironment &env)
283 assert(declaration.Size() >= 2);
284 assert(declaration.Size() <= 12);
286 char user_data[2500];
288 if (!TryConnectRetry(port, user_data, sizeof(user_data), env))
289 return false;
291 char *p = strstr(user_data, "USER DETAILS");
292 if (p != NULL)
293 *p = 0;
295 port.Write('\x18'); // start to upload file
297 if (!port.FullWriteString(user_data, env, 5000) ||
298 !port.FullWriteString("USER DETAILS\r\n--------------\r\n\r\n",
299 env, 1000))
300 return false;
302 WritePair(port, "Pilot Name", declaration.pilot_name.c_str(), env);
303 WritePair(port, "Competition ID", declaration.competition_id.c_str(), env);
304 WritePair(port, "Aircraft Type", declaration.aircraft_type.c_str(), env);
305 WritePair(port, "Aircraft ID",
306 declaration.aircraft_registration.c_str(), env);
308 if (!port.FullWriteString("\r\nFLIGHT DECLARATION\r\n-------------------\r\n\r\n",
309 env, 1000))
310 return false;
312 WritePair(port, "Description", _T("XCSoar task declaration"), env);
314 for (unsigned i = 0; i < 11; i++) {
315 if (env.IsCancelled())
316 return false;
318 if (i+1>= declaration.Size()) {
319 port.FullWriteString("TP LatLon: 0000000N00000000E TURN POINT\r\n",
320 env, 1000);
321 } else {
322 const Waypoint &wp = declaration.GetWaypoint(i);
323 if (i == 0) {
324 if (!EWMicroRecorderWriteWaypoint(port, "Take Off LatLong", wp, env) ||
325 !EWMicroRecorderWriteWaypoint(port, "Start LatLon", wp, env))
326 return false;
327 } else if (i + 1 < declaration.Size()) {
328 if (!EWMicroRecorderWriteWaypoint(port, "TP LatLon", wp, env))
329 return false;
334 const Waypoint &wp = declaration.GetLastWaypoint();
335 if (!EWMicroRecorderWriteWaypoint(port, "Finish LatLon", wp, env) ||
336 !EWMicroRecorderWriteWaypoint(port, "Land LatLon", wp, env) ||
337 env.IsCancelled())
338 return false;
340 port.Write('\x03'); // finish sending user file
342 return port.ExpectString("uploaded successfully", env, 5000);
345 bool
346 EWMicroRecorderDevice::Declare(const Declaration &declaration,
347 const Waypoint *home,
348 OperationEnvironment &env)
350 // Must have at least two, max 12 waypoints
351 if (declaration.Size() < 2 || declaration.Size() > 12)
352 return false;
354 port.StopRxThread();
356 bool success = DeclareInner(port, declaration, env);
358 // go back to NMEA mode
359 port.FullWrite("!!\r\n", 4, env, 500);
361 return success;
365 static Device *
366 EWMicroRecorderCreateOnPort(const DeviceConfig &config, Port &com_port)
368 return new EWMicroRecorderDevice(com_port);
371 const struct DeviceRegister ew_microrecorder_driver = {
372 _T("EW MicroRecorder"),
373 _T("EW microRecorder"),
374 DeviceRegister::DECLARE,
375 EWMicroRecorderCreateOnPort,