1 #ifndef NETWORK_MESSAGE_HPP__ 2 #define NETWORK_MESSAGE_HPP__ 3 4 /* 5 * WSJT-X Message Formats 6 * ====================== 7 * 8 * All messages are written or read using the QDataStream derivatives 9 * defined below, note that we are using the default for floating 10 * point precision which means all are double precision i.e. 64-bit 11 * IEEE format. 12 * 13 * Message is big endian format 14 * 15 * Header format: 16 * 17 * 32-bit unsigned integer magic number 0xadbccbda 18 * 32-bit unsigned integer schema number 19 * 20 * Payload format: 21 * 22 * As per the QDataStream format, see below for version used and 23 * here: 24 * 25 * http://doc.qt.io/qt-5/datastreamformat.html 26 * 27 * for the serialization details for each type, at the time of 28 * writing the above document is for Qt_5_0 format which is buggy 29 * so we use Qt_5_4 format, differences are: 30 * 31 * QDateTime: 32 * QDate qint64 Julian day number 33 * QTime quint32 Milli-seconds since midnight 34 * timespec quint8 0=local, 1=UTC, 2=Offset from UTC 35 * (seconds) 36 * 3=time zone 37 * offset qint32 only present if timespec=2 38 * timezone several-fields only present if timespec=3 39 * 40 * we will avoid using QDateTime fields with time zones for 41 * simplicity. 42 * 43 * Type utf8 is a utf-8 byte string formatted as a QByteArray for 44 * serialization purposes (currently a quint32 size followed by size 45 * bytes, no terminator is present or counted). 46 * 47 * The QDataStream format document linked above is not complete for 48 * the QByteArray serialization format, it is similar to the QString 49 * serialization format in that it differentiates between empty 50 * strings and null strings. Empty strings have a length of zero 51 * whereas null strings have a length field of 0xffffffff. 52 * 53 * 54 * Schema Negotiation 55 * ------------------ 56 * 57 * The NetworkMessage::Builder class specifies a schema number which 58 * may be incremented from time to time. It represents a version of 59 * the underlying encoding schemes used to store data items. Since the 60 * underlying encoding is defined by the Qt project in it's 61 * QDataStream stream operators, it is essential that clients and 62 * servers of this protocol can agree on a common scheme. The 63 * NetworkMessage utility classes below exchange the schema number 64 * actually used. The handling of the schema is backwards compatible 65 * to an extent, so long as clients and servers are written 66 * correctly. For example a server written to any particular schema 67 * version can communicate with a client written to a later schema. 68 * 69 * Schema Version 1:- this schema used the QDataStream::Qt_5_0 version 70 * which is broken. 71 * 72 * Schema Version 2:- this schema uses the QDataStream::Qt_5_2 version. 73 * 74 * Schema Version 3:- this schema uses the QDataStream::Qt_5_4 version. 75 * 76 * 77 * Backward Compatibility 78 * ---------------------- 79 * 80 * It is important that applications developed at different times 81 * remain compatible with this protocol and with older or newer 82 * versions of WSJT-X. This is achieved by both third-party 83 * applications and WSJT-X honouring two basic rules. 84 * 85 * 1. New message types may be added to the protocol in the future, 86 * third-party applications and WSJT-X shall ignore silently any 87 * message types they do not recognize. 88 * 89 * 2. New fields may be added to existing message types, they will 90 * always be added to the end of the existing fields and the number 91 * and type of existing fields shall not change. If a field type 92 * must be changed; a new field will be added and the existing 93 * field will remain. The originator of such a message shall 94 * populate both the new and old field with reasonable 95 * values. Third-party applications and WSJT-X shall ignore 96 * silently any extra data received in datagrams after the fields 97 * they know about. 98 * 99 * Note that these rules are unrelated to the schema number above 100 * whose purpose is to distinguish between non-compatible encodings of 101 * field data types. New message types and extra fields in existing 102 * messages can and will be added without any change in schema number. 103 * 104 * 105 * Message Types 106 * ------------- 107 * 108 * Message Direction Value Type 109 * ------------- --------- ---------------------- ----------- 110 * Heartbeat Out/In 0 quint32 111 * Id (unique key) utf8 112 * Maximum schema number quint32 113 * version utf8 114 * revision utf8 115 * 116 * The heartbeat message shall be sent on a periodic basis every 117 * NetworkMessage::pulse seconds (see below), the WSJT-X 118 * application does that using the MessageClient class. This 119 * message is intended to be used by servers to detect the presence 120 * of a client and also the unexpected disappearance of a client 121 * and by clients to learn the schema negotiated by the server 122 * after it receives the initial heartbeat message from a client. 123 * The message_aggregator reference server does just that using the 124 * MessageServer class. Upon initial startup a client must send a 125 * heartbeat message as soon as is practical, this message is used 126 * to negotiate the maximum schema number common to the client and 127 * server. Note that the server may not be able to support the 128 * client's requested maximum schema number, in which case the 129 * first message received from the server will specify a lower 130 * schema number (never a higher one as that is not allowed). If a 131 * server replies with a lower schema number then no higher than 132 * that number shall be used for all further outgoing messages from 133 * either clients or the server itself. 134 * 135 * Note: the "Maximum schema number" field was introduced at the 136 * same time as schema 3, therefore servers and clients must assume 137 * schema 2 is the highest schema number supported if the Heartbeat 138 * message does not contain the "Maximum schema number" field. 139 * 140 * 141 * Status Out 1 quint32 142 * Id (unique key) utf8 143 * Dial Frequency (Hz) quint64 144 * Mode utf8 145 * DX call utf8 146 * Report utf8 147 * Tx Mode utf8 148 * Tx Enabled bool 149 * Transmitting bool 150 * Decoding bool 151 * Rx DF quint32 152 * Tx DF quint32 153 * DE call utf8 154 * DE grid utf8 155 * DX grid utf8 156 * Tx Watchdog bool 157 * Sub-mode utf8 158 * Fast mode bool 159 * Special Operation Mode quint8 160 * Frequency Tolerance quint32 161 * T/R Period quint32 162 * Configuration Name utf8 163 * Tx Message utf8 164 * 165 * WSJT-X sends this status message when various internal state 166 * changes to allow the server to track the relevant state of each 167 * client without the need for polling commands. The current state 168 * changes that generate status messages are: 169 * 170 * Application start up, 171 * "Enable Tx" button status changes, 172 * dial frequency changes, 173 * changes to the "DX Call" field, 174 * operating mode, sub-mode or fast mode changes, 175 * transmit mode changed (in dual JT9+JT65 mode), 176 * changes to the "Rpt" spinner, 177 * after an old decodes replay sequence (see Replay below), 178 * when switching between Tx and Rx mode, 179 * at the start and end of decoding, 180 * when the Rx DF changes, 181 * when the Tx DF changes, 182 * when settings are exited, 183 * when the DX call or grid changes, 184 * when the Tx watchdog is set or reset, 185 * when the frequency tolerance is changed, 186 * when the T/R period is changed, 187 * when the configuration name changes, 188 * when the message being transmitted changes. 189 * 190 * The Special operation mode is an enumeration that indicates the 191 * setting selected in the WSJT-X "Settings->Advanced->Special 192 * operating activity" panel. The values are as follows: 193 * 194 * 0 -> NONE 195 * 1 -> NA VHF 196 * 2 -> EU VHF 197 * 3 -> FIELD DAY 198 * 4 -> RTTY RU 199 * 5 -> WW DIGI 200 * 6 -> FOX 201 * 7 -> HOUND 202 * 203 * The Frequency Tolerance and T/R period fields may have a value 204 * of the maximum quint32 value which implies the field is not 205 * applicable. 206 * 207 * 208 * Decode Out 2 quint32 209 * Id (unique key) utf8 210 * New bool 211 * Time QTime 212 * snr qint32 213 * Delta time (S) float (serialized as double) 214 * Delta frequency (Hz) quint32 215 * Mode utf8 216 * Message utf8 217 * Low confidence bool 218 * Off air bool 219 * 220 * The decode message is sent when a new decode is completed, in 221 * this case the 'New' field is true. It is also used in response 222 * to a "Replay" message where each old decode in the "Band 223 * activity" window, that has not been erased, is sent in order 224 * as a one of these messages with the 'New' field set to false. 225 * See the "Replay" message below for details of usage. Low 226 * confidence decodes are flagged in protocols where the decoder 227 * has knows that a decode has a higher than normal probability 228 * of being false, they should not be reported on publicly 229 * accessible services without some attached warning or further 230 * validation. Off air decodes are those that result from playing 231 * back a .WAV file. 232 * 233 * 234 * Clear Out/In 3 quint32 235 * Id (unique key) utf8 236 * Window quint8 (In only) 237 * 238 * This message is send when all prior "Decode" messages in the 239 * "Band Activity" window have been discarded and therefore are 240 * no long available for actioning with a "Reply" message. It is 241 * sent when the user erases the "Band activity" window and when 242 * WSJT-X closes down normally. The server should discard all 243 * decode messages upon receipt of this message. 244 * 245 * It may also be sent to a WSJT-X instance in which case it 246 * clears one or both of the "Band Activity" and "Rx Frequency" 247 * windows. The Window argument can be one of the following 248 * values: 249 * 250 * 0 - clear the "Band Activity" window (default) 251 * 1 - clear the "Rx Frequency" window 252 * 2 - clear both "Band Activity" and "Rx Frequency" windows 253 * 254 * 255 * Reply In 4 quint32 256 * Id (target unique key) utf8 257 * Time QTime 258 * snr qint32 259 * Delta time (S) float (serialized as double) 260 * Delta frequency (Hz) quint32 261 * Mode utf8 262 * Message utf8 263 * Low confidence bool 264 * Modifiers quint8 265 * 266 * In order for a server to provide a useful cooperative service 267 * to WSJT-X it is possible for it to initiate a QSO by sending 268 * this message to a client. WSJT-X filters this message and only 269 * acts upon it if the message exactly describes a prior decode 270 * and that decode is a CQ or QRZ message. The action taken is 271 * exactly equivalent to the user double clicking the message in 272 * the "Band activity" window. The intent of this message is for 273 * servers to be able to provide an advanced look up of potential 274 * QSO partners, for example determining if they have been worked 275 * before or if working them may advance some objective like 276 * award progress. The intention is not to provide a secondary 277 * user interface for WSJT-X, it is expected that after QSO 278 * initiation the rest of the QSO is carried out manually using 279 * the normal WSJT-X user interface. 280 * 281 * The Modifiers field allows the equivalent of keyboard 282 * modifiers to be sent "as if" those modifier keys where pressed 283 * while double-clicking the specified decoded message. The 284 * modifier values (hexadecimal) are as follows: 285 * 286 * no modifier 0x00 287 * SHIFT 0x02 288 * CTRL 0x04 CMD on Mac 289 * ALT 0x08 290 * META 0x10 Windows key on MS Windows 291 * KEYPAD 0x20 Keypad or arrows 292 * Group switch 0x40 X11 only 293 * 294 * 295 * QSO Logged Out 5 quint32 296 * Id (unique key) utf8 297 * Date & Time Off QDateTime 298 * DX call utf8 299 * DX grid utf8 300 * Tx frequency (Hz) quint64 301 * Mode utf8 302 * Report sent utf8 303 * Report received utf8 304 * Tx power utf8 305 * Comments utf8 306 * Name utf8 307 * Date & Time On QDateTime 308 * Operator call utf8 309 * My call utf8 310 * My grid utf8 311 * Exchange sent utf8 312 * Exchange received utf8 313 * ADIF Propagation mode utf8 314 * 315 * The QSO logged message is sent to the server(s) when the 316 * WSJT-X user accepts the "Log QSO" dialog by clicking the "OK" 317 * button. 318 * 319 * 320 * Close Out/In 6 quint32 321 * Id (unique key) utf8 322 * 323 * Close is sent by a client immediately prior to it shutting 324 * down gracefully. When sent by a server it requests the target 325 * client to close down gracefully. 326 * 327 * 328 * Replay In 7 quint32 329 * Id (unique key) utf8 330 * 331 * When a server starts it may be useful for it to determine the 332 * state of preexisting clients. Sending this message to each 333 * client as it is discovered will cause that client (WSJT-X) to 334 * send a "Decode" message for each decode currently in its "Band 335 * activity" window. Each "Decode" message sent will have the 336 * "New" flag set to false so that they can be distinguished from 337 * new decodes. After all the old decodes have been broadcast a 338 * "Status" message is also broadcast. If the server wishes to 339 * determine the status of a newly discovered client; this 340 * message should be used. 341 * 342 * 343 * Halt Tx In 8 344 * Id (unique key) utf8 345 * Auto Tx Only bool 346 * 347 * The server may stop a client from transmitting messages either 348 * immediately or at the end of the current transmission period 349 * using this message. 350 * 351 * 352 * Free Text In 9 353 * Id (unique key) utf8 354 * Text utf8 355 * Send bool 356 * 357 * This message allows the server to set the current free text 358 * message content. Sending this message with a non-empty "Text" 359 * field is equivalent to typing a new message (old contents are 360 * discarded) in to the WSJT-X free text message field or "Tx5" 361 * field (both are updated) and if the "Send" flag is set then 362 * clicking the "Now" radio button for the "Tx5" field if tab one 363 * is current or clicking the "Free msg" radio button if tab two 364 * is current. 365 * 366 * It is the responsibility of the sender to limit the length of 367 * the message text and to limit it to legal message 368 * characters. Despite this, it may be difficult for the sender 369 * to determine the maximum message length without reimplementing 370 * the complete message encoding protocol. Because of this is may 371 * be better to allow any reasonable message length and to let 372 * the WSJT-X application encode and possibly truncate the actual 373 * on-air message. 374 * 375 * If the message text is empty the meaning of the message is 376 * refined to send the current free text unchanged when the 377 * "Send" flag is set or to clear the current free text when the 378 * "Send" flag is unset. Note that this API does not include a 379 * command to determine the contents of the current free text 380 * message. 381 * 382 * 383 * WSPRDecode Out 10 quint32 384 * Id (unique key) utf8 385 * New bool 386 * Time QTime 387 * snr qint32 388 * Delta time (S) float (serialized as double) 389 * Frequency (Hz) quint64 390 * Drift (Hz) qint32 391 * Callsign utf8 392 * Grid utf8 393 * Power (dBm) qint32 394 * Off air bool 395 * 396 * The decode message is sent when a new decode is completed, in 397 * this case the 'New' field is true. It is also used in response 398 * to a "Replay" message where each old decode in the "Band 399 * activity" window, that has not been erased, is sent in order 400 * as a one of these messages with the 'New' field set to 401 * false. See the "Replay" message below for details of 402 * usage. The off air field indicates that the decode was decoded 403 * from a played back recording. 404 * 405 * 406 * Location In 11 407 * Id (unique key) utf8 408 * Location utf8 409 * 410 * This message allows the server to set the current current 411 * geographical location of operation. The supplied location is 412 * not persistent but is used as a session lifetime replacement 413 * loction that overrides the Maidenhead grid locater set in the 414 * application settings. The intent is to allow an external 415 * application to update the operating location dynamically 416 * during a mobile period of operation. 417 * 418 * Currently only Maidenhead grid squares or sub-squares are 419 * accepted, i.e. 4- or 6-digit locators. Other formats may be 420 * accepted in future. 421 * 422 * 423 * Logged ADIF Out 12 quint32 424 * Id (unique key) utf8 425 * ADIF text utf8 426 * 427 * The logged ADIF message is sent to the server(s) when the 428 * WSJT-X user accepts the "Log QSO" dialog by clicking the "OK" 429 * button. The "ADIF text" field consists of a valid ADIF file 430 * such that the WSJT-X UDP header information is encapsulated 431 * into a valid ADIF header. E.g.: 432 * 433 * <magic-number><schema-number><type><id><32-bit-count> # binary encoded fields 434 * # the remainder is the contents of the ADIF text field 435 * <adif_ver:5>3.0.7 436 * <programid:6>WSJT-X 437 * <EOH> 438 * ADIF log data fields ...<EOR> 439 * 440 * Note that receiving applications can treat the whole message 441 * as a valid ADIF file with one record without special parsing. 442 * 443 * 444 * Highlight Callsign In 13 quint32 445 * Id (unique key) utf8 446 * Callsign utf8 447 * Background Color QColor 448 * Foreground Color QColor 449 * Highlight last bool 450 * 451 * The server may send this message at any time. The message 452 * specifies the background and foreground color that will be 453 * used to highlight the specified callsign in the decoded 454 * messages printed in the Band Activity panel. The WSJT-X 455 * clients maintain a list of such instructions and apply them to 456 * all decoded messages in the band activity window. To clear 457 * and cancel highlighting send an invalid QColor value for 458 * either or both of the background and foreground fields. When 459 * using this mode the total number of callsign highlighting 460 * requests should be limited otherwise the performance of WSJT-X 461 * decoding may be impacted. A rough rule of thumb might be too 462 * limit the number of active highlighting requests to no more 463 * than 100. 464 * 465 * The "Highlight last" field allows the sender to request that 466 * all instances of "Callsign" in the last period only, instead 467 * of all instances in all periods, be highlighted. 468 * 469 * 470 * SwitchConfiguration In 14 quint32 471 * Id (unique key) utf8 472 * Configuration Name utf8 473 * 474 * The server may send this message at any time. The message 475 * specifies the name of the configuration to switch to. The new 476 * configuration must exist. 477 * 478 * 479 * Configure In 15 quint32 480 * Id (unique key) utf8 481 * Mode utf8 482 * Frequency Tolerance quint32 483 * Submode utf8 484 * Fast Mode bool 485 * T/R Period quint32 486 * Rx DF quint32 487 * DX Call utf8 488 * DX Grid utf8 489 * Generate Messages bool 490 * 491 * The server may send this message at any time. The message 492 * specifies various configuration options. For utf8 string 493 * fields an empty value implies no change, for the quint32 Rx DF 494 * and Frequency Tolerance fields the maximum quint32 value 495 * implies no change. Invalid or unrecognized values will be 496 * silently ignored. 497 */ 498 499 #include <QDataStream> 500 501 #include "pimpl_h.hpp" 502 503 class QIODevice; 504 class QByteArray; 505 class QString; 506 507 namespace NetworkMessage 508 { 509 // NEVER DELETE MESSAGE TYPES 510 enum Type 511 { 512 Heartbeat, 513 Status, 514 Decode, 515 Clear, 516 Reply, 517 QSOLogged, 518 Close, 519 Replay, 520 HaltTx, 521 FreeText, 522 WSPRDecode, 523 Location, 524 LoggedADIF, 525 HighlightCallsign, 526 SwitchConfiguration, 527 Configure, 528 maximum_message_type_ // ONLY add new message types 529 // immediately before here 530 }; 531 532 quint32 constexpr pulse {15}; // seconds 533 534 // 535 // NetworkMessage::Builder - build a message containing serialized Qt types 536 // 537 class Builder 538 : public QDataStream 539 { 540 public: 541 static quint32 constexpr magic {0xadbccbda}; // never change this 542 543 // increment this if a newer Qt schema is required and add decode 544 // logic to the Builder and Reader class implementations 545 #if QT_VERSION >= QT_VERSION_CHECK (5, 4, 0) 546 static quint32 constexpr schema_number {3}; 547 #elif QT_VERSION >= QT_VERSION_CHECK (5, 2, 0) 548 static quint32 constexpr schema_number {2}; 549 #else 550 // Schema 1 (Qt_5_0) is broken 551 #error "Qt version 5.2 or greater required" 552 #endif 553 554 explicit Builder (QIODevice *, Type, QString const& id, quint32 schema); 555 explicit Builder (QByteArray *, Type, QString const& id, quint32 schema); 556 Builder (Builder const&) = delete; 557 Builder& operator = (Builder const&) = delete; 558 559 private: 560 void common_initialization (Type type, QString const& id, quint32 schema); 561 }; 562 563 // 564 // NetworkMessage::Reader - read a message containing serialized Qt types 565 // 566 // Message is as per NetworkMessage::Builder above, the schema() 567 // member may be used to determine the schema of the original 568 // message. 569 // 570 class Reader 571 : public QDataStream 572 { 573 public: 574 explicit Reader (QIODevice *); 575 explicit Reader (QByteArray const&); 576 Reader (Reader const&) = delete; 577 Reader& operator = (Reader const&) = delete; 578 ~Reader (); 579 580 quint32 schema () const; 581 Type type () const; 582 QString id () const; 583 584 private: 585 class impl; 586 pimpl<impl> m_; 587 }; 588 } 589 590 #endif 591