commit
dc6b29d625
@ -0,0 +1,10 @@
|
||||
*.o
|
||||
*.obj
|
||||
x86
|
||||
Debug
|
||||
Release
|
||||
*.user
|
||||
*~
|
||||
*.bak
|
||||
.vs
|
||||
|
||||
@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright (C) 2013 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
|
||||
// 0 and 1 are used for serial
|
||||
const int RFTRANSMIT = 2;
|
||||
const int EXTTRANSMIT = 3;
|
||||
const int HEARTBEAT1 = 4;
|
||||
const int HEARTBEAT2 = 13;
|
||||
const int ACTIVE = 5;
|
||||
const int OUTPUT1 = 6;
|
||||
const int OUTPUT2 = 7;
|
||||
const int OUTPUT3 = 8;
|
||||
const int OUTPUT4 = 9;
|
||||
|
||||
|
||||
const int RFSQUELCH1 = A0;
|
||||
const int RFSQUELCH2 = A1;
|
||||
const int EXTSQUELCH = A2;
|
||||
const int BATTERY = A3;
|
||||
const int DISABLE = A4;
|
||||
|
||||
|
||||
void setup() {
|
||||
// Set up the outut pins with pull ups
|
||||
pinMode(RFSQUELCH1, INPUT);
|
||||
pinMode(RFSQUELCH2, INPUT);
|
||||
pinMode(EXTSQUELCH, INPUT);
|
||||
pinMode(BATTERY, INPUT);
|
||||
pinMode(DISABLE, INPUT);
|
||||
|
||||
digitalWrite(RFSQUELCH1, HIGH);
|
||||
digitalWrite(RFSQUELCH2, HIGH);
|
||||
digitalWrite(EXTSQUELCH, HIGH);
|
||||
digitalWrite(BATTERY, HIGH);
|
||||
digitalWrite(DISABLE, HIGH);
|
||||
|
||||
// Set up the output pins and set low
|
||||
pinMode(RFTRANSMIT, OUTPUT);
|
||||
pinMode(EXTTRANSMIT, OUTPUT);
|
||||
pinMode(HEARTBEAT1, OUTPUT);
|
||||
pinMode(HEARTBEAT2, OUTPUT);
|
||||
pinMode(ACTIVE, OUTPUT);
|
||||
pinMode(OUTPUT1, OUTPUT);
|
||||
pinMode(OUTPUT2, OUTPUT);
|
||||
pinMode(OUTPUT3, OUTPUT);
|
||||
pinMode(OUTPUT4, OUTPUT);
|
||||
|
||||
digitalWrite(RFTRANSMIT, LOW);
|
||||
digitalWrite(EXTTRANSMIT, LOW);
|
||||
digitalWrite(HEARTBEAT1, LOW);
|
||||
digitalWrite(HEARTBEAT2, HIGH);
|
||||
digitalWrite(ACTIVE, LOW);
|
||||
digitalWrite(OUTPUT1, LOW);
|
||||
digitalWrite(OUTPUT2, LOW);
|
||||
digitalWrite(OUTPUT3, LOW);
|
||||
digitalWrite(OUTPUT4, LOW);
|
||||
|
||||
// Set up the serial port
|
||||
Serial.begin(19200);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if (Serial.available() > 0) {
|
||||
int out = Serial.read();
|
||||
|
||||
digitalWrite(RFTRANSMIT, (out & 0x01) == 0x01 ? HIGH : LOW);
|
||||
digitalWrite(EXTTRANSMIT, (out & 0x02) == 0x02 ? HIGH : LOW);
|
||||
digitalWrite(HEARTBEAT1, (out & 0x04) == 0x04 ? HIGH : LOW);
|
||||
digitalWrite(HEARTBEAT2, (out & 0x04) == 0x04 ? HIGH : LOW);
|
||||
digitalWrite(ACTIVE, (out & 0x08) == 0x08 ? HIGH : LOW);
|
||||
digitalWrite(OUTPUT1, (out & 0x10) == 0x10 ? HIGH : LOW);
|
||||
digitalWrite(OUTPUT2, (out & 0x20) == 0x20 ? HIGH : LOW);
|
||||
digitalWrite(OUTPUT3, (out & 0x40) == 0x40 ? HIGH : LOW);
|
||||
digitalWrite(OUTPUT4, (out & 0x80) == 0x80 ? HIGH : LOW);
|
||||
}
|
||||
|
||||
byte val = 0x00;
|
||||
val |= (digitalRead(RFSQUELCH1) == LOW) ? 0x01 : 0x00;
|
||||
val |= (digitalRead(RFSQUELCH2) == LOW) ? 0x02 : 0x00;
|
||||
val |= (digitalRead(EXTSQUELCH) == LOW) ? 0x04 : 0x00;
|
||||
val |= (digitalRead(BATTERY) == LOW) ? 0x08 : 0x00;
|
||||
val |= (digitalRead(DISABLE) == LOW) ? 0x10 : 0x00;
|
||||
|
||||
Serial.write(val);
|
||||
|
||||
delay(20); // 20ms
|
||||
}
|
||||
|
||||
@ -0,0 +1,77 @@
|
||||
Repeater - 20180510
|
||||
===================
|
||||
|
||||
Windows
|
||||
-------
|
||||
|
||||
To use the Repeater control software you will first need to build the latest
|
||||
version of wxWidgets (http://www.wxwidgets.org), the version I used was 2.8.12,
|
||||
I also installed it in the default location which is C:\wxWidgets-2.8.12.
|
||||
|
||||
You will also need a copy of PortAudio (http://www.portaudio.com), I used the
|
||||
latest stable version which is currently 20071207 and it appears to be fine. I
|
||||
put that into the "Visual Studio 2008\Projects" folder alongside the source code
|
||||
for Repeater itself. A skeleton version of PortAudio suitable for using to build
|
||||
Repeater is available from the pcrepeatercontroller Yahoo group in the
|
||||
Files/Windows Extras folder.
|
||||
|
||||
For compiling I use Visual C++ 2008 Express Edition downloaded from Microsoft
|
||||
for free. I recommend that you use the same.
|
||||
|
||||
To build wxWidgets, you simply need to open Visual Studio 2008 using the File ->
|
||||
Open -> Projects/Solutions and load the wx_dll.dsw file to be found in
|
||||
wxWidgets-2.8.12\build\msw directory and then go into Batch Build and select the
|
||||
DLL Unicode Debug and DLL Uncode Release entries for every one, this take a
|
||||
little time! Then build them.
|
||||
|
||||
Unfortunately building the software under Windows also requires the downloading
|
||||
of a very large file from Microsoft, the WDK. Adding this to Visual Studio
|
||||
causes a lot of problems, so I created an extract of the required files from the
|
||||
kit and used that instead. If you do want to build this software from scratch,
|
||||
then download the files HID.zip and WinUSB.zip from the Yahoo! group. Otherwise
|
||||
simply use the supplied binaries.
|
||||
|
||||
The path names for things like wxWidgets and PortAudio are embedded within the
|
||||
Solution and Project preferences, and will need changing if anything other than
|
||||
these default locations are used. The first pass through the compiler will no
|
||||
doubt tell you all that you need to know if there are problems.
|
||||
|
||||
Once you have built the executables, you will need to copy the
|
||||
portaudio_x86.dll, wxbase28u_vc_custom.dll, wxmsw28u_adv_vc_custom.dll,
|
||||
and wxmsw28u_core_vc_custom.dll to the same directory as your newly
|
||||
built executables in order for it to run. If you are running in debug mode then
|
||||
the required wxWidgets files have the names xxx28ud_xxxx instead. These can be
|
||||
found in the wxWidgets-2.8.12\lib\vc_dll directory.
|
||||
|
||||
It is also probable that you'll need to install a copy of the latest Visual C++
|
||||
run-time libraries from Microsoft, if you are not running the Repeater software
|
||||
on the same machine as the development/compilation was done on. To do this you
|
||||
need to copy and run the Vcredist_x86.exe file which is found at
|
||||
<http://www.microsoft.com/en-gb/download/details.aspx?id=5582>
|
||||
|
||||
Linux
|
||||
-----
|
||||
|
||||
You need to ensure that wxGTK, ALSA and libusb-1.0 are already
|
||||
installed on your machine, under Ubuntu these are available from the standard
|
||||
repositories, the version of wxWidgets is adequate. However you should get the
|
||||
latest version of PortAudio from its home and build it from scratch.
|
||||
|
||||
To install them from scratch, you need to get wxGTK from
|
||||
<http://www.wxwidgets.org>, and libusb-1.0 from <http://www.libusb.org/wiki/libusb-1.0>.
|
||||
If you do a "make install" on all of them then they'll be installed in the
|
||||
right places and nothing more needs to be done.
|
||||
|
||||
On the Raspberry Pi you will need to build and install the wiringPi GPIO library
|
||||
which is available from <https://projects.drogon.net/raspberry-pi/wiringpi>.
|
||||
This will allow use the GPIO pins as controller pins for the different repeaters
|
||||
if needed.
|
||||
|
||||
The top level Makefile includes platform specific settings from the file
|
||||
"settings.mk". The default file is for x86 Linux systems. Samples for other
|
||||
platforms are provided by other developers, copy a required version to
|
||||
"settings.mk" when needed.
|
||||
|
||||
To actually build the software, type "make" in the same directory as this file
|
||||
and all should build without errors, there may be a warning or two though. Once
|
||||
compiled log in as root or use the sudo command, and do "make install".
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,638 @@
|
||||
/*
|
||||
* Copyright (C) 2010,2014 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "AMBEFEC.h"
|
||||
|
||||
#include <wx/wx.h>
|
||||
|
||||
static const unsigned int PRNG_TABLE[] = {
|
||||
0x42CC47U, 0x19D6FEU, 0x304729U, 0x6B2CD0U, 0x60BF47U, 0x39650EU, 0x7354F1U, 0xEACF60U, 0x819C9FU, 0xDE25CEU,
|
||||
0xD7B745U, 0x8CC8B8U, 0x8D592BU, 0xF71257U, 0xBCA084U, 0xA5B329U, 0xEE6AFAU, 0xF7D9A7U, 0xBCC21CU, 0x4712D9U,
|
||||
0x4F2922U, 0x14FA37U, 0x5D43ECU, 0x564115U, 0x299A92U, 0x20A9EBU, 0x7B707DU, 0x3BE3A4U, 0x20D95BU, 0x6B085AU,
|
||||
0x5233A5U, 0x99A474U, 0xC0EDCBU, 0xCB5F12U, 0x918455U, 0xF897ECU, 0xE32E3BU, 0xAA7CC2U, 0xB1E7C9U, 0xFC561DU,
|
||||
0xA70DE6U, 0x8DBE73U, 0xD4F608U, 0x57658DU, 0x0E5E56U, 0x458DABU, 0x7E15B8U, 0x376645U, 0x2DFD86U, 0x64EC3BU,
|
||||
0x3F1F60U, 0x3481B4U, 0x4DA00FU, 0x067BCEU, 0x1B68B1U, 0xD19328U, 0xCA03FFU, 0xA31856U, 0xF8EB81U, 0xF9F2F8U,
|
||||
0xA26067U, 0xA91BB6U, 0xF19A59U, 0x9A6148U, 0x8372B6U, 0xC8E86FU, 0x9399DCU, 0x1A0291U, 0x619142U, 0x6DE9FFU,
|
||||
0x367A2CU, 0x7D2511U, 0x6484DAU, 0x2F1F0FU, 0x1E6DB4U, 0x55F6E1U, 0x0EA70AU, 0x061C96U, 0xDD0E45U, 0xB4D738U,
|
||||
0xAF64ABU, 0xE47F42U, 0xFDBE9DU, 0xB684ACU, 0xFE5773U, 0xC1E4A2U, 0x8AFD0DU, 0x932ED4U, 0xD814E3U, 0x81853AU,
|
||||
0x225EECU, 0x7A6945U, 0x31A112U, 0x2AB2EBU, 0x630974U, 0x785AB5U, 0x11E3CEU, 0x4A715BU, 0x402AA0U, 0x199B7DU,
|
||||
0x16C05EU, 0x6F5283U, 0xA4FB10U, 0xBFA8ECU, 0xF633B7U, 0xEC4012U, 0xADD8C9U, 0xD6EB1CU, 0xDD3027U, 0x84A1FAU,
|
||||
0xCF9E19U, 0xD64C80U, 0xBC4557U, 0xA7B62EU, 0x6E2DA1U, 0x311F50U, 0x38C68EU, 0x63D5BFU, 0x486E60U, 0x10BFE1U,
|
||||
0x5BAD1EU, 0x4A4647U, 0x0157F0U, 0x7ACC29U, 0x73BEEAU, 0x2825D7U, 0xA0940CU, 0xFBCFF9U, 0xB05C62U, 0x892426U,
|
||||
0xC6B3DDU, 0xDF3840U, 0x9449B3U, 0xCED3BEU, 0xE7804DU, 0xBC3B90U, 0xF5AA0BU, 0xE6D17EU, 0x2D43B5U, 0x345A04U,
|
||||
0x5EA9DBU, 0x07A202U, 0x0C7134U, 0x45C9FDU, 0x5EDA0AU, 0x310193U, 0x6830C4U, 0x62AA3DU, 0x3B59B2U, 0xB04043U,
|
||||
0xEB975CU, 0x82BCADU, 0x912E62U, 0xD8F7FBU, 0x82C489U, 0x895F54U, 0xF00FE7U, 0xFBBC2AU, 0xA2E771U, 0xE956C4U,
|
||||
0xF6CD1FU, 0x3F8FEAU, 0x0534E1U, 0x4C653CU, 0x17FE8FU, 0x1C4C52U, 0x4515A1U, 0x2E86A9U, 0x3FBD56U, 0x756C87U,
|
||||
0x6ED218U, 0x279179U, 0x7C0AA6U, 0xD53B17U, 0x8EE0C8U, 0x85F291U, 0xD94B36U, 0x9298EFU, 0xAB8318U, 0xE07301U,
|
||||
0xBB68DFU, 0xB2CB7CU, 0xE910A5U, 0xE101D2U, 0x92BB4BU, 0x59E8B4U, 0x407175U, 0x0B026AU, 0x12989BU, 0x792944U,
|
||||
0x2376EDU, 0x2EF5BAU, 0x758663U, 0x7C1ED5U, 0x078D0CU, 0x4EF6ABU, 0x5567F2U, 0x9F7C29U, 0xC68E9CU, 0xC51747U,
|
||||
0xBC6422U, 0xB7EFB9U, 0xECFD44U, 0xA50497U, 0xAF178AU, 0xD68C69U, 0xD97DB5U, 0x82670EU, 0xCBB45BU, 0x508D90U,
|
||||
0x190A25U, 0x63F0FEU, 0x68E3C7U, 0x317A10U, 0x3A09D9U, 0x6B926EU, 0x004237U, 0x1B79C8U, 0x53EA59U, 0x48B3B7U,
|
||||
0x811166U, 0xDE4A79U, 0xF5F988U, 0xAC6057U, 0xE733FEU, 0xFF89ADU, 0xB49830U, 0x8F4BC3U, 0xC6F00EU, 0x9DA135U,
|
||||
0x942FE0U, 0xC71C3BU, 0x4DC78FU, 0x3476C4U, 0x7F6C39U, 0x66BFAAU, 0x298657U, 0x725504U, 0x5B4E89U, 0x01FE72U,
|
||||
0x0835A3U, 0x53269CU, 0x189D4DU, 0x01CDC2U, 0xEA763BU, 0xF3A56DU, 0xB0BCD4U, 0xE80F13U, 0xE355CAU, 0x98C47DU,
|
||||
0x91AB24U, 0xCE38DBU, 0x87A35AU, 0x9CD3A5U, 0xD648F4U, 0xAF7B6FU, 0x24A292U, 0x7D3011U, 0x764B6DU, 0x2DDABEU,
|
||||
0x44D123U, 0x5E22D8U, 0x1FB09DU, 0x04A926U, 0x4F5AF3U, 0x064128U, 0x3DB105U, 0x70AAD6U, 0xAA392FU, 0xA1C4B8U,
|
||||
0xF8C7C0U, 0xD35D0FU, 0x8A2E9EU, 0xC1B761U, 0xDA44F0U, 0x925E8FU, 0x89CF4EU, 0xE8B4D1U, 0xB32728U, 0xB8FE7FU,
|
||||
0x61DCC6U, 0x2A4701U, 0x1614D8U, 0x5DADE2U, 0x46BE37U, 0x0F44DCU, 0x54D549U, 0x5D8E32U, 0x263DAFU, 0x2C237CU,
|
||||
0x75E291U, 0xBE5982U, 0xA74A7FU, 0xC493A4U, 0xDFA131U, 0x967A5AU, 0xCCCB8EU, 0xC1D835U, 0x9A02ECU, 0xF331BBU,
|
||||
0xE8B812U, 0xA3EBC5U, 0xBA507CU, 0x7080ABU, 0x099BC2U, 0x02285DU, 0x59718CU, 0x50C273U, 0x0B1862U, 0x4A1F8CU,
|
||||
0x70A655U, 0x3BF5C2U, 0x666FBBU, 0x6DDE68U, 0x3485C5U, 0x9F161EU, 0xC46F4BU, 0x8CFDF0U, 0x97C625U, 0xDE058EU,
|
||||
0xC59CD3U, 0xAEAE20U, 0xF775BCU, 0xFC647FU, 0xBD9F02U, 0xE70C91U, 0xCC1468U, 0x11E7B7U, 0x1AFC36U, 0x435B49U,
|
||||
0x080398U, 0x139027U, 0x7B63FEU, 0x607AF9U, 0x29E900U, 0x7293D6U, 0x79026FU, 0x00D930U, 0x0BEAF1U, 0xD3614EU,
|
||||
0x90119FU, 0x8B8AE4U, 0xC61969U, 0xBD609AU, 0xB4F247U, 0xEFA954U, 0xE518A9U, 0xBC0362U, 0xD7D0D6U, 0xCE7E8DU,
|
||||
0x856F18U, 0x1C94E3U, 0x578726U, 0x0D5F1DU, 0x24ECC0U, 0x7FF713U, 0x3E26AAU, 0x251D6DU, 0x6A8F14U, 0x53648BU,
|
||||
0x19757AU, 0x40AEB4U, 0xCB9CA5U, 0x90055AU, 0x9956C3U, 0xE2ED34U, 0xAB3C7DU, 0xB126EAU, 0xFA9513U, 0xA3D2C8U,
|
||||
0x886BFDU, 0xD9F836U, 0xD2A2E3U, 0x8D1359U, 0x454804U, 0x5EDBF7U, 0x37637AU, 0x2C3089U, 0x67ABD4U, 0x3E8847U,
|
||||
0x3551BAU, 0x4D6331U, 0x46B8C4U, 0x1D299FU, 0x54120EU, 0x5FC0E1U, 0x86D93BU, 0xE56A0EU, 0xFBB1D5U, 0xB2B600U,
|
||||
0xA94EABU, 0xE05DF6U, 0x9BE605U, 0x90B798U, 0xC92C6BU, 0xC3DE66U, 0x9AC7BDU, 0xD15448U, 0x6A3FD3U, 0x23ADA3U,
|
||||
0x78346CU, 0x7147F5U, 0x2BDC02U, 0x0EAD5BU, 0x553FFCU, 0x1EA425U, 0x07D5F2U, 0x4C4ECBU, 0x554C14U, 0x3EB3F5U,
|
||||
0xE4A26AU, 0xED799BU, 0xB6CA85U, 0xFFD25CU, 0xC421BFU, 0x8F3A22U, 0x96AB51U, 0xDC518CU, 0x895217U, 0x8289F2U,
|
||||
0xF9B8A9U, 0xF0231CU, 0x2BF1C7U, 0x62C80AU, 0x781B39U, 0x1320E5U, 0x4AB156U, 0x41EB8FU, 0x1848E0U, 0x13D771U,
|
||||
0x4886AEU, 0x203C5FU, 0x3B6F40U, 0x76F6A1U, 0xE5457EU, 0xAE1EE7U, 0xD7AC10U, 0xDCB549U, 0x8476EFU, 0x8FC536U,
|
||||
0xD49DE9U, 0x9D0ED8U, 0xA63513U, 0xEFE4A6U, 0xB4DF7DU, 0x3E0D00U, 0x779693U, 0x4CA75EU, 0x0568ADU, 0x527BB0U,
|
||||
0x59C34BU, 0x00109FU, 0x0A0B14U, 0x73FA61U, 0x38E0BAU, 0x23530FU, 0x6A88D4U, 0xB199DDU, 0x98322AU, 0xC260F3U,
|
||||
0xCBF944U, 0x908A0DU, 0xDB11F2U, 0xC28163U, 0xADFABDU, 0xBC694CU, 0xF65243U, 0xAD83BAU, 0xA40D6DU, 0x5F7EF4U,
|
||||
0x16E787U, 0x0DF44AU, 0x460EF1U, 0x5E1F24U, 0x15CC3FU, 0x6C77CAU, 0x676401U, 0x3C9CBDU, 0x359FEEU, 0x6A0413U,
|
||||
0x02F590U, 0x91EE4DU, 0xDA3C3EU, 0xC305A3U, 0x889658U, 0xF14D99U, 0xFA7F86U, 0xA1E677U, 0xE981E8U, 0xF21A10U,
|
||||
0xBB4BD7U, 0x80F1CEU, 0xCB6239U, 0x123BE0U, 0x1D885FU, 0x45921EU, 0x6641E1U, 0x3DE870U, 0x74BBAFU, 0x6F00C6U,
|
||||
0x261055U, 0x7DCBA8U, 0x57787AU, 0x0E2167U, 0x05B28CU, 0xCC8819U, 0x975BE2U, 0xBC52B7U, 0xE5E52CU, 0xEB37C9U,
|
||||
0xB20E12U, 0xF9DD2FU, 0xE8C6FCU, 0x837701U, 0xD8AD82U, 0xD1BE5AU, 0x0B0525U, 0x0244B4U, 0x79FE5BU, 0x322DCAU,
|
||||
0x2B3495U, 0x60876CU, 0x79DCFBU, 0x334C12U, 0x4C7745U, 0x45A4DCU, 0x1E3F23U, 0x175FF2U, 0xC4C0D8U, 0xAFF30DU,
|
||||
0xB72AF6U, 0xFCB96BU, 0xA5C338U, 0xAE5295U, 0xF54946U, 0xDCBABBU, 0x87A1A8U, 0xCF2165U, 0xD4DA9EU, 0x9FC90BU,
|
||||
0x223070U, 0x6922A4U, 0x30B92FU, 0x3348D6U, 0x695B01U, 0x20C038U, 0x1BB2EFU, 0x523B06U, 0x49EC99U, 0x02D7C8U,
|
||||
0x5B4777U, 0x713CA6U, 0xA8AF49U, 0xA3B650U, 0xF84586U, 0xB5DF7FU, 0xAE8CF8U, 0xC72581U, 0x9D3652U, 0x9EEDCFU,
|
||||
0xC75D34U, 0xCC0671U, 0xB5B5CAU, 0xFEAC1FU, 0x677EA4U, 0x2DC5F9U, 0x26D63AU, 0x7F1F86U, 0x142855U, 0x0DF2A8U,
|
||||
0x42E3B3U, 0x195872U, 0x108B8DU, 0x6AB31CU, 0x632063U, 0x307BAAU, 0xFBC83DU, 0xE201C4U, 0xA91393U, 0x90A82AU,
|
||||
0xDAF9E4U, 0x816A55U, 0x88D00AU, 0xD383DBU, 0xFA3A64U, 0xA569A5U, 0xEEE2DEU, 0x76D243U, 0x3D0D90U, 0x649E6DU,
|
||||
0x47E76EU, 0x1C7491U, 0x156E49U, 0x4E9DDEU, 0x0604B7U, 0x3D3720U, 0x76FDD9U, 0x6FEC06U, 0x2417B7U, 0xFD04F8U,
|
||||
0xF29D29U, 0x886F92U, 0xC1744FU, 0xDAC73CU, 0x939EB1U, 0x880C63U, 0xEBE79EU, 0xB2F285U, 0xB86970U, 0xE11ABBU,
|
||||
0xEA822EU, 0x311155U, 0x586AC0U, 0x43F92BU, 0x0A81F6U, 0x5412C5U, 0x5D111CU, 0x26E8CBU, 0x2D7B63U, 0x74213CU,
|
||||
0x3F90CDU, 0x2E8B52U, 0x645883U, 0xDFE36CU, 0x96F375U, 0xDD0882U, 0xC40B1BU, 0x8FD6CCU, 0xB464A5U, 0xFC7F3EU,
|
||||
0xA7AECBU, 0xAA9511U, 0xF10634U, 0xBA5CEFU, 0x83ED32U, 0x483681U, 0x5015DCU, 0x138D3FU, 0x48DEA2U, 0x616571U,
|
||||
0x3AF40CU, 0x33AF97U, 0x681D72U, 0x2246E9U, 0x3BD7B9U, 0x506C46U, 0x0D2FDFU, 0x869338U, 0xDDC061U, 0xD45BD6U,
|
||||
0xAF6A0FU, 0xE7B8C0U, 0xFC2371U, 0xBF102EU, 0xA6C9DFU, 0xEDDA40U, 0x943089U, 0x9FA1BFU, 0x459A66U, 0x0C4995U,
|
||||
0x175108U, 0x7AE243U, 0x6139B6U, 0x2A2A2DU, 0x73D3D8U, 0x79C183U, 0x204A26U, 0x0B3FFDU, 0x5AA420U, 0x111613U,
|
||||
0x8A4FDFU, 0xC3DC2CU, 0xF9A7B5U, 0xB034EAU, 0xEBAC5BU, 0xE0CF94U, 0xBD5465U, 0xF605FAU, 0xCFBEA3U, 0x85AC54U,
|
||||
0x9E55DDU, 0xD7C62AU, 0x0CDD73U, 0x252FCDU, 0x76361CU, 0x7DF5D3U, 0x3546E2U, 0x6E5B39U, 0x67A98CU, 0x1CB247U,
|
||||
0x57231AU, 0x4AD8A9U, 0x01CA74U, 0x191187U, 0xF2208AU, 0xA9AB50U, 0xA0F8A5U, 0xFB403EU, 0xF2D34BU, 0xA9A880U,
|
||||
0xCB393DU, 0xD262EEU, 0x99D0B7U, 0xC04B00U, 0xCB1AC9U, 0xB0B176U, 0x39E3A7U, 0x677EF8U, 0x2ECD58U, 0x359687U,
|
||||
0x7E277EU, 0x473D69U, 0x0CEEB0U, 0x55D557U, 0x5F04CEU, 0x0C8EBDU, 0x25BD60U, 0x7E64DBU, 0xB7771EU, 0xACCC05U,
|
||||
0xE51CF0U, 0xBF2F2AU, 0x90F497U, 0xC9E7D4U, 0xC25F09U, 0x9B9CBAU, 0xD08767U, 0xEB320CU, 0xA36999U, 0x38FB42U,
|
||||
0x7180B3U, 0x22112CU, 0x29AA45U, 0x50F9D2U, 0x1B610AU, 0x0202FDU, 0x4899E4U, 0x57080BU, 0x3E72DAU, 0x65E165U,
|
||||
0x6CFA34U, 0xB70BEBU, 0xBC104AU, 0xE4E295U, 0x8F7BECU, 0x96787FU, 0xD583B2U, 0x9E9740U, 0x870C5DU, 0xECFFA6U,
|
||||
0xF4E433U, 0xBF35F8U, 0xE00F8DU, 0x699C16U, 0x3265EBU, 0x1B6638U, 0x40F515U, 0x0A8DC6U, 0x131E1BU, 0x5845A0U,
|
||||
0x21F670U, 0x2A6E1FU, 0x791D8EU, 0x708651U, 0x2AD7E8U, 0xE37CAFU, 0xD8EE56U, 0x97B3C1U, 0x8E0018U, 0xC51B6FU,
|
||||
0x9CC9E6U, 0xB67019U, 0xEF23C8U, 0xE498F2U, 0xBF9927U, 0xF643ECU, 0xCD7051U, 0x04E902U, 0x563AFFU, 0x5D006CU,
|
||||
0x04D3A1U, 0x0FCA9AU, 0x72794FU, 0x39A2B4U, 0x228231U, 0x6A19EAU, 0x714E96U, 0x18F705U, 0x4324FCU, 0xC83E3BU,
|
||||
0x918D02U, 0xDADCD5U, 0xC2470CU, 0xA135B3U, 0xBABCF2U, 0xF30F4DU, 0xA8549EU, 0xA1C543U, 0xDEFF78U, 0xD42CBCU,
|
||||
0x0DB747U, 0x46C6D2U, 0x5F5C89U, 0x144F60U, 0x6FA6F7U, 0x66350EU, 0x2C0A59U, 0x35DAE0U, 0x7EC12FU, 0x0D32FEU,
|
||||
0x0429C1U, 0x5FB911U, 0xD642AEU, 0x895167U, 0xC3D8B0U, 0xFAAB89U, 0xB1315AU, 0xA8C0A7U, 0xE3DB24U, 0xB84879U,
|
||||
0x913382U, 0xCBA317U, 0x82F8FCU, 0x994BA9U, 0x50C213U, 0x4390CEU, 0x282F5DU, 0x713E30U, 0x7FCDE3U, 0x26565EU,
|
||||
0x2D0485U, 0x56BDD4U, 0x1FAE7BU, 0x0475AAU, 0x4DD555U, 0x17CE4CU, 0x9C1D9BU, 0xE52473U, 0xEEF7E4U, 0xB7CD1DU,
|
||||
0xF45E42U, 0xEF87E3U, 0x87B43CU, 0x986FADU, 0xD16FD2U, 0x8AD403U, 0x8103A8U, 0xD83A75U, 0x33A826U, 0x2BF39BU,
|
||||
0x604049U, 0x7B99A4U, 0x328ABFU, 0x49306AU, 0x407191U, 0x1BEA04U, 0x19D96FU, 0x4001F2U, 0x0FB201U, 0x36E9DCU,
|
||||
0xFD7ADFU, 0xE64326U, 0xAF91F9U, 0xF51249U, 0xDC2B16U, 0x87F8D7U, 0xCCE668U, 0xC517B1U, 0x9E8C46U, 0x97BF5FU,
|
||||
0xED6498U, 0xA67461U, 0x378FF6U, 0x788C8FU, 0x611514U, 0x0AE6F1U, 0x53FC2BU, 0x596F3EU, 0x0216C5U, 0x4B8508U,
|
||||
0x507FBBU, 0x396EE6U, 0x22F535U, 0xE99688U, 0xB10F43U, 0xBA1D36U, 0xC3E2ADU, 0xC07178U, 0x9B28C3U, 0xD69A8BU,
|
||||
0xCD817CU, 0x8570E5U, 0xFEEB12U, 0xF5E8CBU, 0xAC10C4U, 0x270335U, 0x7ED8EAU, 0x156B5BU, 0x0E7A14U, 0x46A0C5U,
|
||||
0x5D937AU, 0x144AA3U, 0x4F79D5U, 0x6CF35CU, 0x31228FU, 0x7A1932U, 0x628E69U, 0xA9D59CU, 0x926517U, 0xDBBEE2U,
|
||||
0x80ADB9U, 0x891424U, 0xD246D7U, 0xD8ED1AU, 0xA17C28U, 0xEA27F5U, 0xF3942EU, 0xB8CE8FU, 0xAB5FD0U, 0x466461U,
|
||||
0x1CB7BEU, 0x152F6FU, 0x4E1CC0U, 0x05D799U, 0x1CE66EU, 0x773DF7U, 0x7EAB00U, 0x249048U, 0x6D41D7U, 0x765A26U,
|
||||
0x1DA9F9U, 0x8431C8U, 0xCF0203U, 0x96C1DEU, 0x90D86DU, 0xCB6A30U, 0xA23193U, 0xB9A24EU, 0xF05B95U, 0xEB48A0U,
|
||||
0xA0D27AU, 0xD8A39FU, 0xD33804U, 0x0A9B79U, 0x01C3AAU, 0x5A5437U, 0x132FD4U, 0x28BC0DU, 0x60253AU, 0x3F57E3U,
|
||||
0x3CCC7CU, 0x65DD9DU, 0x4E26C2U, 0x172572U, 0xDCDDADU, 0xC64E64U, 0x8F5553U, 0x94A68AU, 0xFDBE7DU, 0xA66DE4U,
|
||||
0xADD68BU, 0xF4C75AU, 0xFE0CC1U, 0x873E34U, 0xC8A72FU, 0xDBD0C2U, 0x124B10U, 0x49998DU, 0x40A8FEU, 0x3A3323U,
|
||||
0x316088U, 0x68D95DU, 0x235B06U, 0x3A00B3U, 0x51B178U, 0x4AEA89U, 0x025816U, 0x59C36FU, 0xD092B8U, 0x8B2930U,
|
||||
0xE43AC7U, 0xF5E2DEU, 0xBEC121U, 0xA71AF0U, 0xED8B7FU, 0x94B40EU, 0x9F66D1U, 0xD45D68U, 0xCD8CBFU, 0x8617F6U,
|
||||
0x5F2545U, 0x75FC98U, 0x2EFF62U, 0x674467U, 0x7C959CU, 0x318F09U, 0x0A7CD2U, 0x4967AFU, 0x11D62CU, 0x1A8CD1U,
|
||||
0x431F02U, 0x48A69DU, 0xB3E5ECU, 0xFA7623U, 0xE10E9AU, 0xA99948U, 0xB20215U, 0xD971A6U, 0x80E86BU, 0x8BDA90U,
|
||||
0xD60185U, 0x9D907EU, 0x8FFBFBU, 0xE66920U, 0x7D705DU, 0x3483CEU, 0x6F9833U, 0x646BF1U, 0x1DF3E8U, 0x17E017U,
|
||||
0x4E1BC6U, 0x050A79U, 0x1E8038U, 0x5773E7U, 0x2C685EU, 0xA1BD89U, 0xFB86B0U, 0xF01477U, 0xA16D8EU, 0xCAFE19U,
|
||||
0xD365C1U, 0x9815AEU, 0x839E3FU, 0xCBCDC4U, 0x907611U, 0xB9E70AU, 0xE2BDE7U, 0x2B0E34U, 0x301789U, 0x7BE4DAU,
|
||||
0x477707U, 0x0C2FACU, 0x558C79U, 0x5E9743U, 0x0D4496U, 0x04786DU, 0x7FABE0U, 0x3730B3U, 0x3C014AU, 0xE7DADDU,
|
||||
0xEEE834U, 0x956163U, 0xDCB2FAU, 0xC78905U, 0x8D5BD4U, 0xD0427BU, 0xDBF12BU, 0xA22AB4U, 0xA93B4DU, 0xFA819AU,
|
||||
0xB3D2B3U, 0x287B64U, 0x40289DU, 0x5BB206U, 0x100153U, 0x495CB8U, 0x42CF2DU, 0x3BF4D6U, 0x70248BU, 0x6ABF19U,
|
||||
0x23CCF4U, 0x3C4527U, 0x75761AU, 0x8EACC1U, 0x853F44U, 0xD44EBFU, 0xDED5EEU, 0x87C751U, 0xEC3E80U, 0xF72D6FU,
|
||||
0xBEB676U, 0xE557A1U, 0xEC4D59U, 0xB6BECEU, 0x9DA527U, 0x443078U, 0x0BCAE9U, 0x12D916U, 0x594087U, 0x6033E8U,
|
||||
0x22A831U, 0x7948A2U, 0x70535FU, 0x2BC01CU, 0x62BBA1U, 0x592A7BU, 0x92308EU, 0x8AC395U, 0xC15A50U, 0x9809ABU,
|
||||
0xB3B336U, 0xECB245U, 0xE54998U, 0xBEDA1BU, 0xF681E6U, 0xED35F5U, 0x8E2E0CU, 0x87FDD3U, 0x5CC453U, 0x1556ACU,
|
||||
0x0E85FDU, 0x64AC42U, 0x3D7F8BU, 0x36447CU, 0x6FD665U, 0x640FB2U, 0x3B3C4BU, 0x52A7C4U, 0x48F7B5U, 0x014C2EU,
|
||||
0x9A9FFBU, 0xD19601U, 0xA0250CU, 0xAB7FFFU, 0xF2C822U, 0xB8D1B1U, 0xA302CCU, 0xEAB907U, 0xD1E9B2U, 0x987269U,
|
||||
0xC3411CU, 0xCC8897U, 0x141A42U, 0x3F61B8U, 0x66F2A1U, 0x2DCB56U, 0x3618DFU, 0x778208U, 0x2CB3F1U, 0x0468EEU,
|
||||
0x5F7B1FU, 0x5693D0U, 0x0D8041U, 0x461B3EU, 0xFFECE7U, 0xB4FD50U, 0xA94798U, 0xE314CFU, 0xB88D76U, 0xB17EADU,
|
||||
0xCA7508U, 0xC3E553U, 0x989EA6U, 0xDB0D3DU, 0xC396E8U, 0xA8E683U, 0x717D1EU, 0x7A0EEDU, 0x219730U, 0x288422U,
|
||||
0x736ECFU, 0x1BFF14U, 0x04A4A1U, 0x4F177AU, 0x56092BU, 0x1DD884U, 0x64635DU, 0xEF70EAU, 0xA589B3U, 0xF49B54U,
|
||||
0xFF50CDU, 0xA66312U, 0x8DFA62U, 0xD628FDU, 0x9F131CU, 0x8582C3U, 0xCCF9DAU, 0xF36A29U, 0xB8B2F4U, 0x618157U,
|
||||
0x6A020AU, 0x335999U, 0x79E864U, 0x4272BFU, 0x03259AU, 0x189C40U, 0x51CFB5U, 0x0A752EU, 0x216463U, 0x79BF90U,
|
||||
0x721C0DU, 0xAB47FEU, 0xE4D727U, 0xFDEC28U, 0x963FD9U, 0x8DA646U, 0xC594B7U, 0x9E4FE8U, 0x977E60U, 0xECA597U,
|
||||
0xAF264EU, 0xB61C79U, 0xFDCDA0U, 0x65D64FU, 0x2E61DCU, 0x553881U, 0x5CAA72U, 0x0351FBU, 0x0A400CU, 0x51FB55U,
|
||||
0x3BB9CAU, 0x22223AU, 0x6993B5U, 0x30C8C4U, 0x3B5B1BU, 0xE02B82U, 0xC1B075U, 0x9B23BCU, 0xD25A8BU, 0xC9C852U,
|
||||
0x82A3A9U, 0xBB303CU, 0xF42977U, 0xADDA82U, 0xA64418U, 0xFC55E5U, 0xB5AEE6U, 0x0EBD3BU, 0x4765C8U, 0x4CD655U,
|
||||
0x17DD2EU, 0x562EEBU, 0x6C3770U, 0x25A585U, 0x3E5EDEU, 0x754F6FU, 0x2C94A1U, 0x23A758U, 0x5A3F4FU, 0xD07C96U,
|
||||
0x8BC761U, 0xC254E8U, 0xD92C97U, 0xB0BF06U, 0xEBE0D9U, 0xE25138U, 0xB8CAA7U, 0xBB98DEU, 0xE22109U, 0x896291U,
|
||||
0x10F172U, 0x5BCB2FU, 0x401A94U, 0x0CA141U, 0x77B2BAU, 0x7E6BBFU, 0x255964U, 0x6E82D9U, 0x77130AU, 0x3C3877U,
|
||||
0x04EAF4U, 0x4FD129U, 0x9C40DBU, 0x959BC6U, 0xCEAC2DU, 0xE774FCU, 0xBC6763U, 0xF6DC12U, 0xEB8DCDU, 0xA00664U,
|
||||
0xF9F4B3U, 0xD2EF4AU, 0x895E5DU, 0x800584U, 0x5A972BU, 0x132EFBU, 0x287D84U, 0x63E615U, 0x7297CEU, 0x391D23U,
|
||||
0x608E30U, 0x6AF5CDU, 0x11641EU, 0x5C5E93U, 0x4789E0U, 0x0E903DU, 0x956386U, 0xFEF053U, 0xB6E879U, 0xAD0BACU,
|
||||
0xE41077U, 0xFF83CAU, 0xB47A99U, 0xCD6870U, 0xCE93E7U, 0x96823EU, 0x9D1941U, 0xC4EBD0U, 0x2BF23FU, 0x3031EEU,
|
||||
0x790A71U, 0x229909U, 0x2AC1CEU, 0x717677U, 0x5AEDA0U, 0x039C99U, 0x480646U, 0x515587U, 0x1AEC3CU, 0x296F69U,
|
||||
0xE13492U, 0xBA8607U, 0xB39FCCU, 0xEC4CB1U, 0xA77723U, 0x9EA7DEU, 0xD51C0DU, 0xCD0F00U, 0x86D4FBU, 0xDDF56EU,
|
||||
0xF46F95U, 0x2FBCD4U, 0x268D6BU, 0x7D52B2U, 0x374165U, 0x26F9DCU, 0x4D2A9BU, 0x141163U, 0x1FD2FCU, 0x40CA2DU,
|
||||
0x497952U, 0x3322D3U, 0x7AB32CU, 0xE108F5U, 0xAA5AE2U, 0xB3E31BU, 0xF8B098U, 0x812B65U, 0x8B8936U, 0xD0D08AU,
|
||||
0xD94341U, 0x8A7894U, 0xE3A9AFU, 0xF8377AU, 0xB74481U, 0x6FDD0CU, 0x64EE5FU, 0x3D35A2U, 0x163731U, 0x5F8ECCU,
|
||||
0x045DC7U, 0x0F4616U, 0x57B6E8U, 0x7CAD79U, 0x253E86U, 0x6EC7CFU, 0x7DD478U, 0xB426A1U, 0xCF2D76U, 0xC3BC5FU,
|
||||
0x984780U, 0x935571U, 0xCACCEEU, 0x81BBBFU, 0xB82054U, 0xF371C0U, 0xE9CB3BU, 0xA05826U, 0xFB33F5U, 0x52A218U,
|
||||
0x09B88BU, 0x424BF6U, 0x53D22DU, 0x198198U, 0x043A53U, 0x6F2A06U, 0x34F1BDU, 0x3DC260U, 0x664982U, 0x6FB81BU,
|
||||
0x15A24CU, 0xDE71F5U, 0xC7482AU, 0x8CDFCBU, 0x9505D4U, 0xDE3405U, 0xA5EFFAU, 0xA4FC63U, 0xFE5704U, 0xB387DDU,
|
||||
0xA8BC6AU, 0xC32FB2U, 0x5A7EE5U, 0x11C44CU, 0x489797U, 0x420E62U, 0x19BD79U, 0x30E6BCU, 0x6B6407U, 0x225DDAU,
|
||||
0x398EA9U, 0x703534U, 0x0A64F7U, 0x09FA0AU, 0xD4C910U, 0xDF10E5U, 0x86833EU, 0xCDB99BU, 0xE67A40U, 0xBE631BU,
|
||||
0xB590AEU, 0xEC8B75U, 0xA73BD0U, 0x9CE08BU, 0xD5F35EU, 0x8E0AE5U, 0x061828U, 0x5D835AU, 0x5660C7U, 0x277914U,
|
||||
0x68CAE9U, 0x7190E2U, 0x3A0113U, 0x20FECCU, 0x49ED7DU, 0x127522U, 0x1B06ABU, 0x40855CU, 0x8B9E85U, 0x926FB2U,
|
||||
0xF8F56AU, 0xE186A5U, 0xAA1F14U, 0xF10CCBU, 0xF0F7BAU, 0x8F6735U, 0x867CECU, 0xDC9F1FU, 0x978402U, 0x8E54F1U,
|
||||
0x45EF3CU, 0x7CFC8FU, 0x3705D2U, 0x6C1248U, 0x64C8BDU, 0x3FF976U, 0x566243U, 0x4DA198U, 0x069B45U, 0x1F0AF6U,
|
||||
0x5851BBU, 0x00E248U, 0xAB3BD1U, 0xF2090EU, 0xF9926FU, 0xA2C3F1U, 0xEB7800U, 0xD07B9FU, 0x98A1E6U, 0xC31021U,
|
||||
0xC84BB8U, 0x91D84FU, 0x9AEC96U, 0x6337A9U, 0x288468U, 0x369FB3U, 0x774E06U, 0x6C645DU, 0x05B7A9U, 0x4E2E22U,
|
||||
0x551DFFU, 0x1CC78CU, 0x47D611U, 0x4F2DF2U, 0x343E6FU, 0xBF8514U, 0xE655C1U, 0xAD5E5AU, 0xB4EDBFU, 0xDFB4E4U,
|
||||
0xC1265DU, 0x80DD8BU, 0xDBC852U, 0xD25375U, 0x8920ACU, 0xA2BA53U, 0xFB0BC2U, 0x31401DU, 0x28D33CU, 0x63AAE3U,
|
||||
0x18381AU, 0x11238DU, 0x4AD2E4U, 0x434933U, 0x195BABU, 0x56A058U, 0x6FB105U, 0x2C5AAEU, 0x35C97BU, 0xFED9A0U,
|
||||
0xA52295U, 0x8D314EU, 0xD6ECA3U, 0x9F5E30U, 0x84456DU, 0xCFB6DEU, 0xD6AF03U, 0xBD2CE9U, 0xE556FCU, 0xEEC707U,
|
||||
0xB71CD6U, 0x382F59U, 0x43B720U, 0x02E4F7U, 0x195F4EU, 0x51CC99U, 0x0AA550U, 0x013767U, 0x786CBEU, 0x73DD01U,
|
||||
0x2AC6D1U, 0x61159EU, 0x7BA92FU, 0x92BAF4U, 0x896109U, 0xC0521AU, 0x9F9AF7U, 0x942924U, 0xC532B9U, 0xEFE3C2U,
|
||||
0xA6D807U, 0xFD0ABCU, 0xF69369U, 0xAFA033U, 0x44738EU, 0x5D694DU, 0x17C8F0U, 0x0C93A3U, 0x45207AU, 0x1EF9C5U,
|
||||
0x37EB04U, 0x6850FBU, 0x6305EAU, 0x3B9E15U, 0x782DC4U, 0x41774BU, 0x8AF633U, 0xD18DE4U, 0xD81E5DU, 0x83A69AU,
|
||||
0x8AF583U, 0xF06E7CU, 0xBB5FADU, 0xA28416U, 0xE99653U, 0xF06D88U, 0x9FEC35U, 0xC4F7E6U, 0x4C059AU, 0x1F1C19U,
|
||||
0x56EFC4U, 0x4D743FU, 0x24612AU, 0x3F9BD1U, 0x748814U, 0x2C13AFU, 0x27F276U, 0x5EE861U, 0x553B88U, 0x0E0A5FU,
|
||||
0xC791E6U, 0xD8E2B0U, 0x907A69U, 0xABE9C6U, 0xE09217U, 0xB10168U, 0xBA48F9U, 0xE3FA26U, 0x8861CFU, 0x9230D8U,
|
||||
0xDB8B21U, 0xC099B2U, 0x09644FU, 0x52F704U, 0x79AC90U, 0x201F6BU, 0x2E17BEU, 0x77C495U, 0x3CFF48U, 0x172E9BU,
|
||||
0x4E9426U, 0x0D8775U, 0x145E98U, 0x5E6D03U, 0xC5F6D6U, 0xAC242DU, 0xF70D3CU, 0xFEDED2U, 0xA5C543U, 0xAE74BCU,
|
||||
0xD62EE5U, 0x9D9D72U, 0x80029BU, 0xCB534CU, 0x90E175U, 0x19BAAAU, 0x6A3B6BU, 0x6280D4U, 0x39D385U, 0x724B7AU,
|
||||
0x6B78E2U, 0x00A321U, 0x19101CU, 0x5248CFU, 0x0ADB30U, 0x01F0A9U, 0x5A21CEU, 0xB73A17U, 0xACC880U, 0xE55179U,
|
||||
0xFE42A6U, 0xB4B987U, 0xC5AF58U, 0xCE1688U, 0x97C533U, 0x9CCE76U, 0xC73F8DU, 0x8E2510U, 0xB4B6C3U, 0x7D4FFEU,
|
||||
0x665C3DU, 0x2DC7C0U, 0x70B55BU, 0x5B2C2EU, 0x025FF5U, 0x49D470U, 0x53448AU, 0x1A3FD7U, 0x09AC64U, 0x60BDBDU,
|
||||
0x3B467AU, 0xB0D043U, 0xE98B9CU, 0xE33A2DU, 0x9A21E2U, 0xD1C3B3U, 0xCA5A0CU, 0x8709DDU, 0xDCB222U, 0xF5A3AAU,
|
||||
0xBF79DDU, 0xA44A04U, 0xEDD193U, 0x3E006AU, 0x373B21U, 0x4CF994U, 0x47C04FU, 0x1F53DAU, 0x5488A1U, 0x4DB86CU,
|
||||
0x2623DFU, 0x7D7402U, 0x70CF50U, 0x2B9EFDU, 0x232426U, 0xF8A7D3U, 0x91FEC8U, 0x8A4D39U, 0xC117F6U, 0xD0866FU,
|
||||
0x9B3D18U, 0xE36EC1U, 0xE8F576U, 0xB3C5BFU, 0xBA1629U, 0xE1BD50U, 0xA8EC8FU, 0x17763EU, 0x5D45F1U, 0x049CA0U,
|
||||
0x0F8F1FU, 0x5630C6U, 0x7DE225U, 0x26FB38U, 0x6F08CBU, 0x7D0316U, 0x34B28DU, 0x2F68E9U, 0xC47B72U, 0x9DC287U,
|
||||
0x96915CU, 0xCF0B41U, 0x85F8A2U, 0xBAE17FU, 0xF372CCU, 0xE81991U, 0xA1894AU, 0xFAF2EBU, 0xF16134U, 0x89F845U,
|
||||
0x0A8ADBU, 0x53153AU, 0x1806E5U, 0x03FF7CU, 0x6A7C0BU, 0x312692U, 0x399775U, 0x628CACU, 0x6D7FB3U, 0x34EE42U,
|
||||
0x5FF49DU, 0x56073CU, 0x8D1C67U, 0x87CDBBU, 0xDEE708U, 0xB574D5U, 0xA4ADB6U, 0xEF9E2BU, 0xF605D0U, 0xBD7545U,
|
||||
0xE6EE0EU, 0xCE39FBU, 0x950260U, 0xD8929DU, 0x43D9CEU, 0x086A47U, 0x31B3B1U, 0x7AA068U, 0x221ADFU, 0x294B86U,
|
||||
0x72F049U, 0x73E3F8U, 0x083927U, 0x418856U, 0x5AC3C9U, 0x105020U, 0xC969B7U, 0xE2BBEEU, 0xBF2019U, 0xB41181U,
|
||||
0xEFCA6AU, 0xA6FD3FU, 0xBC27A4U, 0xD53651U, 0xCE9D9AU, 0x854EA7U, 0xDC5E74U, 0xDFE5A9U, 0x26B61AU, 0x6C0D57U,
|
||||
0x77DCECU, 0x3EC639U, 0x2575C3U, 0x682CD6U, 0x13AF1DU, 0x1855ECU, 0x404473U, 0x4BDF8AU, 0x12ACDDU, 0xF93754U,
|
||||
0xE207A3U, 0xABD87AU, 0xF04B45U, 0xF03284U, 0xABB05BU, 0x80ABEBU, 0xD95AB4U, 0x92C10DU, 0x8FD2CEU, 0xC42833U,
|
||||
0xEC3920U, 0x37C2FDU, 0x7C5106U, 0x654883U, 0x2EAAF8U, 0x37B12DU, 0x5C20B6U, 0x065B42U, 0x07C909U, 0x5C12B4U,
|
||||
0x152367U, 0x2EB4FAU, 0x65CF19U, 0xFC5F40U, 0xB294FFU, 0xEBA72EU, 0xE03ED1U, 0x9B6CD0U, 0x92D70FU, 0xC944F6U,
|
||||
0x801D60U, 0x9AAE19U, 0xF1F4DEU, 0xA85547U, 0xAB4EB8U, 0x729DE9U, 0x792456U, 0x223697U, 0x4BED0CU, 0x55DE71U,
|
||||
0x1C03A2U, 0x07910FU, 0x4CAADCU, 0x356BA0U, 0x3E5033U, 0x67C3EEU, 0x2D9B05U, 0xB62810U, 0xFFF3EBU, 0xC4E03EU,
|
||||
0x8558A5U, 0xDE0B48U, 0xD5905BU, 0x8D71A2U, 0xA26A75U, 0xFBD8ECU, 0xB08982U, 0xAB1253U, 0xE2A1ECU, 0x79FB3FU,
|
||||
0x116E52U, 0x4A15C9U, 0x43861CU, 0x188FE7U, 0x537DF2U, 0x62E619U, 0x29D7C0U, 0x310C57U, 0x7A1F2EU, 0x25E5B8U,
|
||||
0xAC7451U, 0xC76F86U, 0xDE9C9FU, 0x959460U, 0xCF27B1U, 0xC6FC1EU, 0xBDEDCFU, 0xF416B0U, 0xEF0429U, 0xA49FEEU,
|
||||
0xBDEA17U, 0xFF7104U, 0x06A3F8U, 0x0D8A63U, 0x5219A6U, 0x5B62DDU, 0x00F348U, 0x6969B3U, 0x731A6EU, 0x38816DU,
|
||||
0x61D090U, 0x6A6343U, 0x33F9FEU, 0x18B8A5U, 0xC30340U, 0x8B10DAU, 0x98E80BU, 0xD1FB74U, 0xEA20F5U, 0xA5930AU,
|
||||
0xFC8E93U, 0xF75CC4U, 0xAF673DU, 0xA4E6BAU, 0xDF3D43U, 0x960F9CU, 0x0DD68DU, 0x44E572U, 0x1F7EB2U, 0x35AD09U,
|
||||
0x6C9554U, 0x6746A7U, 0x365D3AU, 0x7DFCF9U, 0x64A6C4U, 0x0B351FU, 0x118CEAU, 0x58DF61U, 0x836434U, 0x8A36CFU,
|
||||
0xF1AB5BU, 0xBA18A0U, 0xA343EDU, 0xE8C27EU, 0xF0F887U, 0xBB2B50U, 0xC03A69U, 0xC9C1A6U, 0x9A5317U, 0x9368C8U,
|
||||
0x5CB919U, 0x26A226U, 0x2F01EFU, 0x74D919U, 0x3DCA80U, 0x2631D7U, 0x6D223EU, 0x54BAA1U, 0x1E4950U, 0x47520BU,
|
||||
0x4CA79EU, 0x97BC75U, 0xBE3EA8U, 0xED479BU, 0xA4D446U, 0xBA4FF5U, 0xF13C39U, 0xE8A46AU, 0x83D7D7U, 0xDA4C0CU,
|
||||
0xD1DDF9U, 0x8AA7F2U, 0xC22427U, 0x793DDCU, 0x30CE45U, 0x2B5522U, 0x6007FBU, 0x39BE6CU, 0x32AD95U, 0x42560BU,
|
||||
0x4D426AU, 0x16D1B5U, 0x5F3A04U, 0x442BDBU, 0x2DF082U, 0xF6C225U, 0xFE59FCU, 0xA5880FU, 0xAEB312U, 0xF761C9U,
|
||||
0x9C582CU, 0x85CBB7U, 0xCE00C3U, 0xD43118U, 0x9DAB9DU, 0xEAF866U, 0xE3437BU, 0x381288U, 0x738955U, 0x6A3BF6U,
|
||||
0x2066ABU, 0x19D570U, 0x52DEC1U, 0x090E1EU, 0x00B5FFU, 0x5BE6E1U, 0x727D38U, 0x284CCFU, 0x639656U, 0xFA8531U,
|
||||
0xBD3CA8U, 0xD4EF77U, 0xCFC586U, 0x841489U, 0x9C0F78U, 0xD7BCA7U, 0x8E671EU, 0xA5774DU, 0xFE8481U, 0xF79F32U,
|
||||
0xAC0AEFU, 0x65F09CU, 0x5FF301U, 0x144ACAU, 0x0D193FU, 0x468224U, 0x13F0D1U, 0x18694AU, 0x63FA87U, 0x2B81F4U,
|
||||
0x30106DU, 0x790A9BU, 0xE2E952U, 0x8970CDU, 0xD003BCU, 0xDB9963U, 0x838AD2U, 0x88731DU, 0xD1E064U, 0xBAFFF3U,
|
||||
0xA10F2AU, 0xEC049DU, 0xBFD7D4U, 0xB7EE2BU, 0x4C7CBBU, 0x478760U, 0x1E9415U, 0x554D9EU, 0x4C7E6BU, 0x07E4B0U,
|
||||
0x3D35ADU, 0x741E4EU, 0x2F8D93U, 0x26FC20U, 0x7D667DU, 0x16B586U, 0x8B8E02U, 0xC91FD9U, 0xD0456CU, 0x9BF237U,
|
||||
0xC0EBCEU, 0xE92849U, 0xB29390U, 0xBBC3E7U, 0xE1787EU, 0xAA6B81U, 0x93B040U, 0xD8005FU, 0x411BAEU, 0x0AC870U,
|
||||
0x51F1D1U, 0x5D328EU, 0x362837U, 0x6799E0U, 0x6C4239U, 0x37711AU, 0x3EABC7U, 0x45BA3CU, 0x0D01A9U, 0x16D6F2U,
|
||||
0xDDCF17U, 0xC46D8CU, 0x8F3670U, 0xF6A723U, 0xFD5CBCU, 0xA74F5DU, 0xEAF582U, 0xF1A43BU, 0x903768U, 0x8B0CC5U,
|
||||
0xC0DC16U, 0x9957CBU, 0x1324F0U, 0x4ABD25U, 0x61AECEU, 0x38545AU, 0x73C701U, 0x68FEF4U, 0x212D6FU, 0x5B3382U,
|
||||
0x52C2D1U, 0x09494CU, 0x065ABFU, 0xDFA126U, 0x9CB149U, 0xA56A98U, 0xEE5927U, 0xF4C0F6U, 0xBD33B8U, 0xE62901U,
|
||||
0xCFB8D6U, 0x94D32FU, 0x9F40B8U, 0xC69AF1U, 0x8CAB0EU, 0x15309FU, 0x7E6360U, 0x21DA31U, 0x2848BAU, 0x733747U,
|
||||
0x72A6D4U, 0x08EDA8U, 0x435F7BU, 0x5A4CD6U, 0x119505U, 0x082658U, 0x433DE3U, 0xB8ED26U, 0xB0D6DDU, 0xEB05C8U,
|
||||
0xA2BC13U, 0xA9BEEAU, 0xD6656DU, 0xDF5614U, 0x848F82U, 0xC41C5BU, 0xDF26A4U, 0x94F7A5U, 0xADCC5AU, 0x665B8BU,
|
||||
0x3F1234U, 0x34A0EDU, 0x6E7BAAU, 0x076813U, 0x1CD1C4U, 0x55833DU, 0x4E1836U, 0x03A9E2U, 0x58F219U, 0x72418CU,
|
||||
0x2B09F7U, 0xA89A72U, 0xF1A1A9U, 0xBA7254U, 0x81EA47U, 0xC899BAU, 0xD20279U, 0x9B13C4U, 0xC0E09FU, 0xCB7E4BU,
|
||||
0xB25FF0U, 0xF98431U, 0xE4974EU, 0x2E6CD7U, 0x35FC00U, 0x5CE7A9U, 0x07147EU, 0x060D07U, 0x5D9F98U, 0x56E449U,
|
||||
0x0E65A6U, 0x659EB7U, 0x7C8D49U, 0x371790U, 0x6C6623U, 0xE5FD6EU, 0x9E6EBDU, 0x921600U, 0xC985D3U, 0x82DAEEU,
|
||||
0x9B7B25U, 0xD0E0F0U, 0xE1924BU, 0xAA091EU, 0xF158F5U, 0xF9E369U, 0x22F1BAU, 0x4B28C7U, 0x509B54U, 0x1B80BDU,
|
||||
0x024162U, 0x497B53U, 0x01A88CU, 0x3E1B5DU, 0x7502F2U, 0x6CD12BU, 0x27EB1CU, 0x7E7AC5U, 0xDDA113U, 0x8596BAU,
|
||||
0xCE5EEDU, 0xD54D14U, 0x9CF68BU, 0x87A54AU, 0xEE1C31U, 0xB58EA4U, 0xBFD55FU, 0xE66482U, 0xE93FA1U, 0x90AD7CU,
|
||||
0x5B04EFU, 0x405713U, 0x09CC48U, 0x13BFEDU, 0x522736U, 0x2914E3U, 0x22CFD8U, 0x7B5E05U, 0x3061E6U, 0x29B37FU,
|
||||
0x43BAA8U, 0x5849D1U, 0x91D25EU, 0xCEE0AFU, 0xC73971U, 0x9C2A40U, 0xB7919FU, 0xEF401EU, 0xA452E1U, 0xB5B9B8U,
|
||||
0xFEA80FU, 0x8533D6U, 0x8C4115U, 0xD7DA28U, 0x5F6BF3U, 0x043006U, 0x4FA39DU, 0x76DBD9U, 0x394C22U, 0x20C7BFU,
|
||||
0x6BB64CU, 0x312C41U, 0x187FB2U, 0x43C46FU, 0x0A55F4U, 0x192E81U, 0xD2BC4AU, 0xCBA5FBU, 0xA15624U, 0xF85DFDU,
|
||||
0xF38ECBU, 0xBA3602U, 0xA125F5U, 0xCEFE6CU, 0x97CF3BU, 0x9D55C2U, 0xC4A64DU, 0x4FBFBCU, 0x1468A3U, 0x7D4352U,
|
||||
0x6ED19DU, 0x270804U, 0x7D3B76U, 0x76A0ABU, 0x0FF018U, 0x0443D5U, 0x5D188EU, 0x16A93BU, 0x0932E0U, 0xC07015U,
|
||||
0xFACB1EU, 0xB39AC3U, 0xE80170U, 0xE3B3ADU, 0xBAEA5EU, 0xD17956U, 0xC042A9U, 0x8A9378U, 0x912DE7U, 0xD86E86U,
|
||||
0x83F559U, 0x2AC4E8U, 0x711F37U, 0x7A0D6EU, 0x26B4C9U, 0x6D6710U, 0x547CE7U, 0x1F8CFEU, 0x449720U, 0x4D3483U,
|
||||
0x16EF5AU, 0x1EFE2DU, 0x6D44B4U, 0xA6174BU, 0xBF8E8AU, 0xF4FD95U, 0xED6764U, 0x86D6BBU, 0xDC8912U, 0xD10A45U,
|
||||
0x8A799CU, 0x83E12AU, 0xF872F3U, 0xB10954U, 0xAA980DU, 0x6083D6U, 0x397163U, 0x3AE8B8U, 0x439BDDU, 0x481046U,
|
||||
0x1302BBU, 0x5AFB68U, 0x50E875U, 0x297396U, 0x26824AU, 0x7D98F1U, 0x344BA4U, 0xAF726FU, 0xE6F5DAU, 0x9C0F01U,
|
||||
0x971C38U, 0xCE85EFU, 0xC5F626U, 0x946D91U, 0xFFBDC8U, 0xE48637U, 0xAC15A6U, 0xB74C48U, 0x7EEE99U, 0x21B586U,
|
||||
0x0A0677U, 0x539FA8U, 0x18CC01U, 0x007652U, 0x4B67CFU, 0x70B43CU, 0x390FF1U, 0x625ECAU, 0x6BD01FU, 0x38E3C4U,
|
||||
0xB23870U, 0xCB893BU, 0x8093C6U, 0x994055U, 0xD679A8U, 0x8DAAFBU, 0xA4B176U, 0xFE018DU, 0xF7CA5CU, 0xACD963U,
|
||||
0xE762B2U, 0xFE323DU, 0x1589C4U, 0x0C5A92U, 0x4F432BU, 0x17F0ECU, 0x1CAA35U, 0x673B82U, 0x6E54DBU, 0x31C724U,
|
||||
0x785CA5U, 0x632C5AU, 0x29B70BU, 0x508490U, 0xDB5D6DU, 0x82CFEEU, 0x89B492U, 0xD22541U, 0xBB2EDCU, 0xA1DD27U,
|
||||
0xE04F62U, 0xFB56D9U, 0xB0A50CU, 0xF9BED7U, 0xC24EFAU, 0x8F5529U, 0x55C6D0U, 0x5E3B47U, 0x07383FU, 0x2CA2F0U,
|
||||
0x75D161U, 0x3E489EU, 0x25BB0FU, 0x6DA170U, 0x7630B1U, 0x174B2EU, 0x4CD8D7U, 0x470180U, 0x9E2339U, 0xD5B8FEU,
|
||||
0xE9EB27U, 0xA2521DU, 0xB941C8U, 0xF0BB23U, 0xAB2AB6U, 0xA271CDU, 0xD9C250U, 0xD3DC83U, 0x8A1D6EU, 0x41A67DU,
|
||||
0x58B580U, 0x3B6C5BU, 0x205ECEU, 0x6985A5U, 0x333471U, 0x3E27CAU, 0x65FD13U, 0x0CCE44U, 0x1747EDU, 0x5C143AU,
|
||||
0x45AF83U, 0x8F7F54U, 0xF6643DU, 0xFDD7A2U, 0xA68E73U, 0xAF3D8CU, 0xF4E79DU, 0xB5E073U, 0x8F59AAU, 0xC40A3DU,
|
||||
0x999044U, 0x922197U, 0xCB7A3AU, 0x60E9E1U, 0x3B90B4U, 0x73020FU, 0x6839DAU, 0x21FA71U, 0x3A632CU, 0x5151DFU,
|
||||
0x088A43U, 0x039B80U, 0x4260FDU, 0x18F36EU, 0x33EB97U, 0xEE1848U, 0xE503C9U, 0xBCA4B6U, 0xF7FC67U, 0xEC6FD8U,
|
||||
0x849C01U, 0x9F8506U, 0xD616FFU, 0x8D6C29U, 0x86FD90U, 0xFF26CFU, 0xF4150EU, 0x2C9EB1U, 0x6FEE60U, 0x74751BU,
|
||||
0x39E696U, 0x429F65U, 0x4B0DB8U, 0x1056ABU, 0x1AE756U, 0x43FC9DU, 0x282F29U, 0x318172U, 0x7A90E7U, 0xE36B1CU,
|
||||
0xA878D9U, 0xF2A0E2U, 0xDB133FU, 0x8008ECU, 0xC1D955U, 0xDAE292U, 0x9570EBU, 0xAC9B74U, 0xE68A85U, 0xBF514BU,
|
||||
0x34635AU, 0x6FFAA5U, 0x66A93CU, 0x1D12CBU, 0x54C382U, 0x4ED915U, 0x056AECU, 0x5C2D37U, 0x779402U, 0x2607C9U,
|
||||
0x2D5D1CU, 0x72ECA6U, 0xBAB7FBU, 0xA12408U, 0xC89C85U, 0xD3CF76U, 0x98542BU, 0xC177B8U, 0xCAAE45U, 0xB29CCEU,
|
||||
0xB9473BU, 0xE2D660U, 0xABEDF1U, 0xA03F1EU, 0x7926C4U, 0x1A95F1U, 0x044E2AU, 0x4D49FFU, 0x56B154U, 0x1FA209U,
|
||||
0x6419FAU, 0x6F4867U, 0x36D394U, 0x3C2199U, 0x653842U, 0x2EABB7U, 0x95C02CU, 0xDC525CU, 0x87CB93U, 0x8EB80AU,
|
||||
0xD423FDU, 0xF152A4U, 0xAAC003U, 0xE15BDAU, 0xF82A0DU, 0xB3B134U, 0xAAB3EBU, 0xC14C0AU, 0x1B5D95U, 0x128664U,
|
||||
0x49357AU, 0x002DA3U, 0x3BDE40U, 0x70C5DDU, 0x6954AEU, 0x23AE73U, 0x76ADE8U, 0x7D760DU, 0x064756U, 0x0FDCE3U,
|
||||
0xD40E38U, 0x9D37F5U, 0x87E4C6U, 0xECDF1AU, 0xB54EA9U, 0xBE1470U, 0xE7B71FU, 0xEC288EU, 0xB77951U, 0xDFC3A0U,
|
||||
0xC490BFU, 0x89095EU, 0x1ABA81U, 0x51E118U, 0x2853EFU, 0x234AB6U, 0x7B8910U, 0x703AC9U, 0x2B6216U, 0x62F127U,
|
||||
0x59CAECU, 0x101B59U, 0x4B2082U, 0xC1F2FFU, 0x88696CU, 0xB358A1U, 0xFA9752U, 0xAD844FU, 0xA63CB4U, 0xFFEF60U,
|
||||
0xF5F4EBU, 0x8C059EU, 0xC71F45U, 0xDCACF0U, 0x95772BU, 0x4E6622U, 0x67CDD5U, 0x3D9F0CU, 0x3406BBU, 0x6F75F2U,
|
||||
0x24EE0DU, 0x3D7E9CU, 0x520542U, 0x4396B3U, 0x09ADBCU, 0x527C45U, 0x5BF292U, 0xA0810BU, 0xE91878U, 0xF20BB5U,
|
||||
0xB9F10EU, 0xA1E0DBU, 0xEA33C0U, 0x938835U, 0x989BFEU, 0xC36342U, 0xCA6011U, 0x95FBECU, 0xFD0A6FU, 0x6E11B2U,
|
||||
0x25C3C1U, 0x3CFA5CU, 0x7769A7U, 0x0EB266U, 0x058079U, 0x5E1988U, 0x167E17U, 0x0DE5EFU, 0x44B428U, 0x7F0E31U,
|
||||
0x349DC6U, 0xEDC41FU, 0xE277A0U, 0xBA6DE1U, 0x99BE1EU, 0xC2178FU, 0x8B4450U, 0x90FF39U, 0xD9EFAAU, 0x823457U,
|
||||
0xA88785U, 0xF1DE98U, 0xFA4D73U, 0x3377E6U, 0x68A41DU, 0x43AD48U, 0x1A1AD3U, 0x14C836U, 0x4DF1EDU, 0x0622D0U,
|
||||
0x173903U, 0x7C88FEU, 0x27527DU, 0x2E41A5U, 0xF4FADAU, 0xFDBB4BU, 0x8601A4U, 0xCDD235U, 0xD4CB6AU, 0x9F7893U,
|
||||
0x862304U, 0xCCB3EDU, 0xB388BAU, 0xBA5B23U, 0xE1C0DCU, 0xE8A00DU, 0x3B3F27U, 0x500CF2U, 0x48D509U, 0x034694U,
|
||||
0x5A3CC7U, 0x51AD6AU, 0x0AB6B9U, 0x234544U, 0x785E57U, 0x30DE9AU, 0x2B2561U, 0x6036F4U, 0xDDCF8FU, 0x96DD5BU,
|
||||
0xCF46D0U, 0xCCB729U, 0x96A4FEU, 0xDF3FC7U, 0xE44D10U, 0xADC4F9U, 0xB61366U, 0xFD2837U, 0xA4B888U, 0x8EC359U,
|
||||
0x5750B6U, 0x5C49AFU, 0x07BA79U, 0x4A2080U, 0x517307U, 0x38DA7EU, 0x62C9ADU, 0x611230U, 0x38A2CBU, 0x33F98EU,
|
||||
0x4A4A35U, 0x0153E0U, 0x98815BU, 0xD23A06U, 0xD929C5U, 0x80E079U, 0xEBD7AAU, 0xF20D57U, 0xBD1C4CU, 0xE6A78DU,
|
||||
0xEF7472U, 0x954CE3U, 0x9CDF9CU, 0xCF8455U, 0x0437C2U, 0x1DFE3BU, 0x56EC6CU, 0x6F57D5U, 0x25061BU, 0x7E95AAU,
|
||||
0x772FF5U, 0x2C7C24U, 0x05C59BU, 0x5A965AU, 0x111D21U, 0x892DBCU, 0xC2F26FU, 0x9B6192U, 0xB81891U, 0xE38B6EU,
|
||||
0xEA91B6U, 0xB16221U, 0xF9FB48U, 0xC2C8DFU, 0x890226U, 0x9013F9U, 0xDBE848U, 0x02FB07U, 0x0D62D6U, 0x77906DU,
|
||||
0x3E8BB0U, 0x2538C3U, 0x6C614EU, 0x77F39CU, 0x141861U, 0x4D0D7AU, 0x47968FU, 0x1EE544U, 0x157DD1U, 0xCEEEAAU,
|
||||
0xA7953FU, 0xBC06D4U, 0xF57E09U, 0xABED3AU, 0xA2EEE3U, 0xD91734U, 0xD2849CU, 0x8BDEC3U, 0xC06F32U, 0xD174ADU,
|
||||
0x9BA77CU, 0x201C93U, 0x690C8AU, 0x22F77DU, 0x3BF4E4U, 0x702933U, 0x4B9B5AU, 0x0380C1U, 0x585134U, 0x556AEEU,
|
||||
0x0EF9CBU, 0x45A310U, 0x7C12CDU, 0xB7C97EU, 0xAFEA23U, 0xEC72C0U, 0xB7215DU, 0x9E9A8EU, 0xC50BF3U, 0xCC5068U,
|
||||
0x97E28DU, 0xDDB916U, 0xC42846U, 0xAF93B9U, 0xF2D020U, 0x796CC7U, 0x223F9EU, 0x2BA429U, 0x5095F0U, 0x18473FU,
|
||||
0x03DC8EU, 0x40EFD1U, 0x593620U, 0x1225BFU, 0x6BCF76U, 0x605E40U, 0xBA6599U, 0xF3B66AU, 0xE8AEF7U, 0x851DBCU,
|
||||
0x9EC649U, 0xD5D5D2U, 0x8C2C27U, 0x863E7CU, 0xDFB5D9U, 0xF4C002U, 0xA55BDFU, 0xEEE9ECU, 0x75B020U, 0x3C23D3U,
|
||||
0x06584AU, 0x4FCB15U, 0x1453A4U, 0x1F306BU, 0x42AB9AU, 0x09FA05U, 0x30415CU, 0x7A53ABU, 0x61AA22U, 0x2839D5U,
|
||||
0xF3228CU, 0xDAD032U, 0x89C9E3U, 0x820A2CU, 0xCAB91DU, 0x91A4C6U, 0x985673U, 0xE34DB8U, 0xA8DCE5U, 0xB52756U,
|
||||
0xFE358BU, 0xE6EE78U, 0x0DDF75U, 0x5654AFU, 0x5F075AU, 0x04BFC1U, 0x0D2CB4U, 0x56577FU, 0x34C6C2U, 0x2D9D11U,
|
||||
0x662F48U, 0x3FB4FFU, 0x34E536U, 0x4F4E89U, 0xC61C58U, 0x988107U, 0xD132A7U, 0xCA6978U, 0x81D881U, 0xB8C296U,
|
||||
0xF3114FU, 0xAA2AA8U, 0xA0FB31U, 0xF37142U, 0xDA429FU, 0x819B24U, 0x4888E1U, 0x5333FAU, 0x1AE30FU, 0x40D0D5U,
|
||||
0x6F0B68U, 0x36182BU, 0x3DA0F6U, 0x646345U, 0x2F7898U, 0x14CDF3U, 0x5C9666U, 0xC704BDU, 0x8E7F4CU, 0xDDEED3U,
|
||||
0xD655BAU, 0xAF062DU, 0xE49EF5U, 0xFDFD02U, 0xB7661BU, 0xA8F7F4U, 0xC18D25U, 0x9A1E9AU, 0x9305CBU, 0x48F414U,
|
||||
0x43EFB5U, 0x1B1D6AU, 0x708413U, 0x698780U, 0x2A7C4DU, 0x6168BFU, 0x78F3A2U, 0x130059U, 0x0B1BCCU, 0x40CA07U,
|
||||
0x1FF072U, 0x9663E9U, 0xCD9A14U, 0xE499C7U, 0xBF0AEAU, 0xF57239U, 0xECE1E4U, 0xA7BA5FU, 0xDE098FU, 0xD591E0U,
|
||||
0x86E271U, 0x8F79AEU, 0xD52817U, 0x1C8350U, 0x2711A9U, 0x684C3EU, 0x71FFE7U, 0x3AE490U, 0x633619U, 0x498FE6U,
|
||||
0x10DC37U, 0x1B670DU, 0x4066D8U, 0x09BC13U, 0x328FAEU, 0xFB16FDU, 0xA9C500U, 0xA2FF93U, 0xFB2C5EU, 0xF03565U,
|
||||
0x8D86B0U, 0xC65D4BU, 0xDD7DCEU, 0x95E615U, 0x8EB169U, 0xE708FAU, 0xBCDB03U, 0x37C1C4U, 0x6E72FDU, 0x25232AU,
|
||||
0x3DB8F3U, 0x5ECA4CU, 0x45430DU, 0x0CF0B2U, 0x57AB61U, 0x5E3ABCU, 0x210087U, 0x2BD343U, 0xF248B8U, 0xB9392DU,
|
||||
0xA0A376U, 0xEBB09FU, 0x905908U, 0x99CAF1U, 0xD3F5A6U, 0xCA251FU, 0x813ED0U, 0xF2CD01U, 0xFBD63EU, 0xA046EEU,
|
||||
0x29BD51U, 0x76AE98U, 0x3C274FU, 0x055476U, 0x4ECEA5U, 0x573F58U, 0x1C24DBU, 0x47B786U, 0x6ECC7DU, 0x345CE8U,
|
||||
0x7D0703U, 0x66B456U, 0xAF3DECU, 0xBC6F31U, 0xD7D0A2U, 0x8EC1CFU, 0x80321CU, 0xD9A9A1U, 0xD2FB7AU, 0xA9422BU,
|
||||
0xE05184U, 0xFB8A55U, 0xB22AAAU, 0xE831B3U, 0x63E264U, 0x1ADB8CU, 0x11081BU, 0x4832E2U, 0x0BA1BDU, 0x10781CU,
|
||||
0x784BC3U, 0x679052U, 0x2E902DU, 0x752BFCU, 0x7EFC57U, 0x27C58AU, 0xCC57D9U, 0xD40C64U, 0x9FBFB6U, 0x84665BU,
|
||||
0xCD7540U, 0xB6CF95U, 0xBF8E6EU, 0xE415FBU, 0xE62690U, 0xBFFE0DU, 0xF04DFEU, 0xC91623U, 0x028520U, 0x19BCD9U,
|
||||
0x506E06U, 0x0AEDB6U, 0x23D4E9U, 0x780728U, 0x331997U, 0x3AE84EU, 0x6173B9U, 0x6840A0U, 0x129B67U, 0x598B9EU,
|
||||
0xC87009U, 0x877370U, 0x9EEAEBU, 0xF5190EU, 0xAC03D4U, 0xA690C1U, 0xFDE93AU, 0xB47AF7U, 0xAF8044U, 0xC69119U,
|
||||
0xDD0ACAU, 0x166977U, 0x4EF0BCU, 0x45E2C9U, 0x3C1D52U, 0x3F8E87U, 0x64D73CU, 0x296574U, 0x327E83U, 0x7A8F1AU,
|
||||
0x0114EDU, 0x0A1734U, 0x53EF3BU, 0xD8FCCAU, 0x812715U, 0xEA94A4U, 0xF185EBU, 0xB95F3AU, 0xA26C85U, 0xEBB55CU,
|
||||
0xB0862AU, 0x930CA3U, 0xCEDD70U, 0x85E6CDU, 0x9D7196U, 0x562A63U, 0x6D9AE8U, 0x24411DU, 0x7F5246U, 0x76EBDBU,
|
||||
0x2DB928U, 0x2712E5U, 0x5E83D7U, 0x15D80AU, 0x0C6BD1U, 0x473170U, 0x54A02FU, 0xB99B9EU, 0xE34841U, 0xEAD090U,
|
||||
0xB1E33FU, 0xFA2866U, 0xE31991U, 0x88C208U, 0x8154FFU, 0xDB6FB7U, 0x92BE28U, 0x89A5D9U, 0xE25606U, 0x7BCE37U,
|
||||
0x30FDFCU, 0x693E21U, 0x6F2792U, 0x3495CFU, 0x5DCE6CU, 0x465DB1U, 0x0FA46AU, 0x14B75FU, 0x5F2D85U, 0x275C60U,
|
||||
0x2CC7FBU, 0xF56486U, 0xFE3C55U, 0xA5ABC8U, 0xECD02BU, 0xD743F2U, 0x9FDAC5U, 0xC0A81CU, 0xC33383U, 0x9A2262U,
|
||||
0xB1D93DU, 0xE8DA8DU, 0x232252U, 0x39B19BU, 0x70AAACU, 0x6B5975U, 0x024182U, 0x59921BU, 0x522974U, 0x0B38A5U,
|
||||
0x01F33EU, 0x78C1CBU, 0x3758D0U, 0x242F3DU, 0xEDB4EFU, 0xB66672U, 0xBF5701U, 0xC5CCDCU, 0xCE9F77U, 0x9726A2U,
|
||||
0xDCA4F9U, 0xC5FF4CU, 0xAE4E87U, 0xB51576U, 0xFDA7E9U, 0xA63C90U, 0x2F6D47U, 0x74D6CFU, 0x1BC538U, 0x0A1D21U,
|
||||
0x413EDEU, 0x58E50FU, 0x127480U, 0x6B4BF1U, 0x60992EU, 0x2BA297U, 0x327340U, 0x79E809U, 0xA0DABAU, 0x8A0367U,
|
||||
0xD1009DU, 0x98BB98U, 0x836A63U, 0xCE70F6U, 0xF5832DU, 0xB69850U, 0xEE29D3U, 0xE5732EU, 0xBCE0FDU, 0xB75962U,
|
||||
0x4C1A13U, 0x0589DCU, 0x1EF165U, 0x5666B7U, 0x4DFDEAU, 0x268E59U, 0x7F1794U, 0x74256FU, 0x29FE7AU, 0x626F81U,
|
||||
0x700404U, 0x1996DFU, 0x828FA2U, 0xCB7C31U, 0x9067CCU, 0x9B940EU, 0xE20C17U, 0xE81FE8U, 0xB1E439U, 0xFAF586U,
|
||||
0xE17FC7U, 0xA88C18U, 0xD397A1U, 0x5E4276U, 0x04794FU, 0x0FEB88U, 0x5E9271U, 0x3501E6U, 0x2C9A3EU, 0x67EA51U,
|
||||
0x7C61C0U, 0x34323BU, 0x6F89EEU, 0x4618F5U, 0x1D4218U, 0xD4F1CBU, 0xCFE876U, 0x841B25U, 0xB888F8U, 0xF3D053U,
|
||||
0xAA7386U, 0xA168BCU, 0xF2BB69U, 0xFB8792U, 0x80541FU, 0xC8CF4CU, 0xC3FEB5U, 0x182522U, 0x1117CBU, 0x6A9E9CU,
|
||||
0x234D05U, 0x3876FAU, 0x72A42BU, 0x2FBD84U, 0x240ED4U, 0x5DD54BU, 0x56C4B2U, 0x057E65U, 0x4C2D4CU, 0xD7849BU,
|
||||
0xBFD762U, 0xA44DF9U, 0xEFFEACU, 0xB6A347U, 0xBD30D2U, 0xC40B29U, 0x8FDB74U, 0x9540E6U, 0xDC330BU, 0xC3BAD8U,
|
||||
0x8A89E5U, 0x71533EU, 0x7AC0BBU, 0x2BB140U, 0x212A11U, 0x7838AEU, 0x13C17FU, 0x08D290U, 0x414989U, 0x1AA85EU,
|
||||
0x13B2A6U, 0x494131U, 0x625AD8U, 0xBBCF87U, 0xF43516U, 0xED26E9U, 0xA6BF78U, 0x9FCC17U, 0xDD57CEU, 0x86B75DU,
|
||||
0x8FACA0U, 0xD43FE3U, 0x9D445EU, 0xA6D584U, 0x6DCF71U, 0x753C6AU, 0x3EA5AFU, 0x67F654U, 0x4C4CC9U, 0x134DBAU,
|
||||
0x1AB667U, 0x4125E4U, 0x097E19U, 0x12CA0AU, 0x71D1F3U, 0x78022CU, 0xA33BACU, 0xEAA953U, 0xF17A02U, 0x9B53BDU,
|
||||
0xC28074U, 0xC9BB83U, 0x90299AU, 0x9BF04DU, 0xC4C3B4U, 0xAD583BU, 0xB7084AU, 0xFEB3D1U, 0x656004U, 0x2E69FEU,
|
||||
0x5FDAF3U, 0x548000U, 0x0D37DDU, 0x472E4EU, 0x5CFD33U, 0x1546F8U, 0x2E164DU, 0x678D96U, 0x3CBEE3U, 0x337768U,
|
||||
0xEBE5BDU, 0xC09E47U, 0x990D5EU, 0xD234A9U, 0xC9E720U, 0x887DF7U, 0xD34C0EU, 0xFB9711U, 0xA084E0U, 0xA96C2FU,
|
||||
0xF27FBEU, 0xB9E4C1U, 0x001318U, 0x4B02AFU, 0x56B867U, 0x1CEB30U, 0x477289U, 0x4E8152U, 0x358AF7U, 0x3C1AACU,
|
||||
0x676159U, 0x24F2C2U, 0x3C6917U, 0x57197CU, 0x8E82E1U, 0x85F112U, 0xDE68CFU, 0xD77BDDU, 0x8C9130U, 0xE400EBU,
|
||||
0xFB5B5EU, 0xB0E885U, 0xA9F6D4U, 0xE2277BU, 0x9B9CA2U, 0x108F15U, 0x5A764CU, 0x0B64ABU, 0x00AF32U, 0x599CEDU,
|
||||
0x72059DU, 0x29D702U, 0x60ECE3U, 0x7A7D3CU, 0x330625U, 0x0C95D6U, 0x474D0BU, 0x9E7EA8U, 0x95FDF5U, 0xCCA666U,
|
||||
0x86179BU, 0xBD8D40U, 0xFCDA65U, 0xE763BFU, 0xAE304AU, 0xF58AD1U, 0xDE9B9CU, 0x86406FU, 0x8DE3F2U, 0x54B801U,
|
||||
0x1B28D8U, 0x0213D7U, 0x69C026U, 0x7259B9U, 0x3A6B48U, 0x61B017U, 0x68819FU, 0x135A68U, 0x50D9B1U, 0x49E386U,
|
||||
0x02325FU, 0x9A29B0U, 0xD19E23U, 0xAAC77EU, 0xA3558DU, 0xFCAE04U, 0xF5BFF3U, 0xAE04AAU, 0xC44635U, 0xDDDDC5U,
|
||||
0x966C4AU, 0xCF373BU, 0xC4A4E4U, 0x1FD47DU, 0x3E4F8AU, 0x64DC43U, 0x2DA574U, 0x3637ADU, 0x7D5C56U, 0x44CFC3U,
|
||||
0x0BD688U, 0x52257DU, 0x59BBE7U, 0x03AA1AU, 0x4A5119U, 0xF142C4U, 0xB89A37U, 0xB329AAU, 0xE822D1U, 0xA9D114U,
|
||||
0x93C88FU, 0xDA5A7AU, 0xC1A121U, 0x8AB090U, 0xD36B5EU, 0xDC58A7U, 0xA5C0B0U, 0x2F8369U, 0x74389EU, 0x3DAB17U,
|
||||
0x26D368U, 0x4F40F9U, 0x141F26U, 0x1DAEC7U, 0x473558U, 0x446721U, 0x1DDEF6U, 0x769D6EU, 0xEF0E8DU, 0xA434D0U,
|
||||
0xBFE56BU, 0xF35EBEU, 0x884D45U, 0x819440U, 0xDAA69BU, 0x917D26U, 0x88ECF5U, 0xC3C788U, 0xFB150BU, 0xB02ED6U,
|
||||
0x63BF24U, 0x6A6439U, 0x3153D2U, 0x188B03U, 0x43989CU, 0x0923EDU, 0x147232U, 0x5FF99BU, 0x060B4CU, 0x2D10B5U,
|
||||
0x76A1A2U, 0x7FFA7BU, 0xA568D4U, 0xECD104U, 0xD7827BU, 0x9C19EAU, 0x8D6831U, 0xC6E2DCU, 0x9F71CFU, 0x950A32U,
|
||||
0xEE9BE1U, 0xA3A16CU, 0xB8761FU, 0xF16FC2U, 0x6A9C79U, 0x010FACU, 0x491786U, 0x52F453U, 0x1BEF88U, 0x007C35U,
|
||||
0x4B8566U, 0x32978FU, 0x316C18U, 0x697DC1U, 0x62E6BEU, 0x3B142FU, 0xD40DC0U, 0xCFCE11U, 0x86F58EU, 0xDD66F6U,
|
||||
0xD53E31U, 0x8E8988U, 0xA5125FU, 0xFC6366U, 0xB7F9B9U, 0xAEAA78U, 0xE513C3U, 0xD69096U, 0x1ECB6DU, 0x4579F8U,
|
||||
0x4C6033U, 0x13B34EU, 0x5888DCU, 0x615821U, 0x2AE3F2U, 0x32F0FFU, 0x792B04U, 0x220A91U, 0x0B906AU, 0xD0432BU,
|
||||
0xD97294U, 0x82AD4DU, 0xC8BE9AU, 0xD90623U, 0xB2D564U, 0xEBEE9CU, 0xE02D03U, 0xBF35D2U, 0xB686ADU, 0xCCDD2CU,
|
||||
0x854CD3U, 0x1EF70AU, 0x55A51DU, 0x4C1CE4U, 0x074F67U, 0x7ED49AU, 0x7476C9U, 0x2F2F75U, 0x26BCBEU, 0x75876BU,
|
||||
0x1C5650U, 0x07C885U, 0x48BB7EU, 0x9022F3U, 0x9B11A0U, 0xC2CA5DU, 0xE9C8CEU, 0xA07133U, 0xFBA238U, 0xF0B9E9U,
|
||||
0xA84917U, 0x835286U, 0xDAC179U, 0x913830U, 0x822B87U, 0x4BD95EU, 0x30D289U, 0x3C43A0U, 0x67B87FU, 0x6CAA8EU,
|
||||
0x353311U, 0x7E4440U, 0x47DFABU, 0x0C8E3FU, 0x1634C4U, 0x5FA7D9U, 0x04CC0AU, 0xAD5DE7U, 0xF64774U, 0xBDB409U,
|
||||
0xAC2DD2U, 0xE67E67U, 0xFBC5ACU, 0x90D5F9U, 0xCB0E42U, 0xC23D9FU, 0x99B67DU, 0x9047E4U, 0xEA5DB3U, 0x218E0AU,
|
||||
0x38B7D5U, 0x732034U, 0x6AFA2BU, 0x21CBFAU, 0x5A1005U, 0x5B039CU, 0x01A8FBU, 0x4C7822U, 0x574395U, 0x3CD04DU,
|
||||
0xA5811AU, 0xEE3BB3U, 0xB76868U, 0xBDF19DU, 0xE64286U, 0xCF1943U, 0x949BF8U, 0xDDA225U, 0xC67156U, 0x8FCACBU,
|
||||
0xF59B08U, 0xF605F5U, 0x2B36EFU, 0x20EF1AU, 0x797CC1U, 0x324664U, 0x1985BFU, 0x419CE4U, 0x4A6F51U, 0x13748AU,
|
||||
0x58C42FU, 0x631F74U, 0x2A0CA1U, 0x71F51AU, 0xF9E7D7U, 0xA27CA5U, 0xA99F38U, 0xD886EBU, 0x973516U, 0x8E6F1DU,
|
||||
0xC5FEECU, 0xDF0133U, 0xB61282U, 0xED8ADDU, 0xE4F954U, 0xBF7AA3U, 0x74617AU, 0x6D904DU, 0x070A95U, 0x1E795AU,
|
||||
0x55E0EBU, 0x0EF334U, 0x0F0845U, 0x7098CAU, 0x798313U, 0x2360E0U, 0x687BFDU, 0x71AB0EU, 0xBA10C3U, 0x830370U,
|
||||
0xC8FA2DU, 0x93EDB7U, 0x9B3742U, 0xC00689U, 0xA99DBCU, 0xB25E67U, 0xF964BAU, 0xE0F509U, 0xA7AE44U, 0xFF1DB7U,
|
||||
0x54C42EU, 0x0DF6F1U, 0x066D90U, 0x5D3C0EU, 0x1487FFU, 0x2F8460U, 0x675E19U, 0x3CEFDEU, 0x37B447U, 0x6E27B0U,
|
||||
0x651369U, 0x9CC856U, 0xD77B97U, 0xC9604CU, 0x88B1F9U, 0x939BA2U, 0xFA4856U, 0xB1D1DDU, 0xAAE200U, 0xE33873U,
|
||||
0xB829EEU, 0xB0D20DU, 0xCBC190U, 0x407AEBU, 0x19AA3EU, 0x52A1A5U, 0x4B1240U, 0x204B1BU, 0x3ED9A2U, 0x7F2274U,
|
||||
0x2437ADU, 0x2DAC8AU, 0x76DF53U, 0x5D45ACU, 0x04F43DU, 0xCEBFE2U, 0xD72CC3U, 0x9C551CU, 0xE7C7E5U, 0xEEDC72U,
|
||||
0xB52D1BU, 0xBCB6CCU, 0xE6A454U, 0xA95FA7U, 0x904EFAU, 0xD3A551U, 0xCA3684U, 0x01265FU, 0x5ADD6AU, 0x72CEB1U,
|
||||
0x29135CU, 0x60A1CFU, 0x7BBA92U, 0x304921U, 0x2950FCU, 0x42D316U, 0x1AA903U, 0x1138F8U, 0x48E329U, 0xC7D0A6U,
|
||||
0xBC48DFU, 0xFD1B08U, 0xE6A0B1U, 0xAE3366U, 0xF55AAFU, 0xFEC898U, 0x879341U, 0x8C22FEU, 0xD5392EU, 0x9EEA61U,
|
||||
0x8456D0U, 0x6D450BU, 0x769EF6U, 0x3FADE5U, 0x606508U, 0x6BD6DBU, 0x3ACD46U, 0x101C3DU, 0x5927F8U, 0x02F543U,
|
||||
0x096C96U, 0x505FCCU, 0xBB8C71U, 0xA296B2U, 0xE8370FU, 0xF36C5CU, 0xBADF85U, 0xE1063AU, 0xC814FBU, 0x97AF04U,
|
||||
0x9CFA15U, 0xC461EAU, 0x87D23BU, 0xBE88B4U, 0x7509CCU, 0x2E721BU, 0x27E1A2U, 0x7C5965U, 0x750A7CU, 0x0F9183U,
|
||||
0x44A052U, 0x5D7BE9U, 0x1669ACU, 0x0F9277U, 0x6013CAU, 0x3B0819U, 0xB3FA65U, 0xE0E3E6U, 0xA9103BU, 0xB28BC0U,
|
||||
0xDB9ED5U, 0xC0642EU, 0x8B77EBU, 0xD3EC50U, 0xD80D89U, 0xA1179EU, 0xAAC477U, 0xF1F5A0U, 0x386E19U, 0x271D4FU,
|
||||
0x6F8596U, 0x541639U, 0x1F6DE8U, 0x4EFE97U, 0x45B706U, 0x1C05D9U, 0x779E30U, 0x6DCF27U, 0x2474DEU, 0x3F664DU,
|
||||
0xF69BB0U, 0xAD08FBU, 0x86536FU, 0xDFE094U, 0xD1E841U, 0x883B6AU, 0xC300B7U, 0xE8D164U, 0xB16BD9U, 0xF2788AU,
|
||||
0xEBA167U, 0xA192FCU, 0x3A0929U, 0x53DBD2U, 0x08F2C3U, 0x01212DU, 0x5A3ABCU, 0x518B43U, 0x29D11AU, 0x62628DU,
|
||||
0x7FFD64U, 0x34ACB3U, 0x6F1E8AU, 0xE64555U, 0x95C494U, 0x9D7F2BU, 0xC62C7AU, 0x8DB485U, 0x94871DU, 0xFF5CDEU,
|
||||
0xE6EFE3U, 0xADB730U, 0xF524CFU, 0xFE0F56U, 0xA5DE31U, 0x48C5E8U, 0x53377FU, 0x1AAE86U, 0x01BD59U, 0x4B4678U,
|
||||
0x3A50A7U, 0x31E977U, 0x683ACCU, 0x633189U, 0x38C072U, 0x71DAEFU, 0x4B493CU, 0x82B001U, 0x99A3C2U, 0xD2383FU,
|
||||
0x8F4AA4U, 0xA4D3D1U, 0xFDA00AU, 0xB62B8FU, 0xACBB75U, 0xE5C028U, 0xF6539BU, 0x9F4242U, 0xC4B985U, 0x4F2FBCU,
|
||||
0x167463U, 0x1CC5D2U, 0x65DE1DU, 0x2E3C4CU, 0x35A5F3U, 0x78F622U, 0x234DDDU, 0x0A5C55U, 0x408622U, 0x5BB5FBU,
|
||||
0x122E6CU, 0xC1FF95U, 0xC8C4DEU, 0xB3066BU, 0xB83FB0U, 0xE0AC25U, 0xAB775EU, 0xB24793U, 0xD9DC20U, 0x828BFDU,
|
||||
0x8F30AFU, 0xD46102U, 0xDCDBD9U, 0x07582CU, 0x6E0137U, 0x75B2C6U, 0x3EE809U, 0x2F7990U, 0x64C2E7U, 0x1C913EU,
|
||||
0x170A89U, 0x4C3A40U, 0x45E9D6U, 0x1E42AFU, 0x571370U, 0xE889C1U, 0xA2BA0EU, 0xFB635FU, 0xF070E0U, 0xA9CF39U,
|
||||
0x821DDAU, 0xD904C7U, 0x90F734U, 0x82FCE9U, 0xCB4D72U, 0xD09716U, 0x3B848DU, 0x623D78U, 0x696EA3U, 0x30F4BEU,
|
||||
0x7A075DU, 0x451E80U, 0x0C8D33U, 0x17E66EU, 0x5E76B5U, 0x050D14U, 0x0E9ECBU, 0x7607BAU, 0xF57524U, 0xACEAC5U,
|
||||
0xE7F91AU, 0xFC0083U, 0x9583F4U, 0xCED96DU, 0xC6688AU, 0x9D7353U, 0x92804CU, 0xCB11BDU, 0xA00B62U, 0xA9F8C3U,
|
||||
0x72E398U, 0x783244U, 0x2118F7U, 0x4A8B2AU, 0x5B5249U, 0x1061D4U, 0x09FA2FU, 0x428ABAU, 0x1911F1U, 0x31C604U,
|
||||
0x6AFD9FU, 0x276D62U, 0xBC2631U, 0xF795B8U, 0xCE4C4EU, 0x855F97U, 0xDDE520U, 0xD6B479U, 0x8D0FB6U, 0x8C1C07U,
|
||||
0xF7C6D8U, 0xBE77A9U, 0xA53C36U, 0xEFAFDFU, 0x369648U, 0x1D4411U, 0x40DFE6U, 0x4BEE7EU, 0x103595U, 0x5902C0U,
|
||||
0x43D85BU, 0x2AC9AEU, 0x316265U, 0x7AB158U, 0x23A18BU, 0x201A56U, 0xD949E5U, 0x93F2A8U, 0x882313U, 0xC139C6U,
|
||||
0xDA8A3CU, 0x97D329U, 0xEC50E2U, 0xE7AA13U, 0xBFBB8CU, 0xB42075U, 0xED5322U, 0x06C8ABU, 0x1DF85CU, 0x542785U,
|
||||
0x0FB4BAU, 0x0FCD7BU, 0x544FA4U, 0x7F5414U, 0x26A54BU, 0x6D3EF2U, 0x702D31U, 0x3BD7CCU, 0x13C6DFU, 0xC83D02U,
|
||||
0x83AEF9U, 0x9AB77CU, 0xD15507U, 0xC84ED2U, 0xA3DF49U, 0xF9A4BDU, 0xF836F6U, 0xA3ED4BU, 0xEADC98U, 0xD14B05U,
|
||||
0x9A30E6U, 0x03A0BFU, 0x4D6B00U, 0x1458D1U, 0x1FC12EU, 0x64932FU, 0x6D28F0U, 0x36BB09U, 0x7FE29FU, 0x6551E6U,
|
||||
0x0E0B21U, 0x57AAB8U, 0x54B147U, 0x8D6216U, 0x86DBA9U, 0xDDC968U, 0xB412F3U, 0xAA218EU, 0xE3FC5DU, 0xF86EF0U,
|
||||
0xB35523U, 0xCA945FU, 0xC1AFCCU, 0x983C11U, 0xD264FAU, 0x49D7EFU, 0x000C14U, 0x3B1FC1U, 0x7AA75AU, 0x21F4B7U,
|
||||
0x2A6FA4U, 0x728E5DU, 0x5D958AU, 0x042713U, 0x4F767DU, 0x54EDACU, 0x1D5E13U, 0x8604C0U, 0xEE91ADU, 0xB5EA36U,
|
||||
0xBC79E3U, 0xE77018U, 0xAC820DU, 0x9D19E6U, 0xD6283FU, 0xCEF3A8U, 0x85E0D1U, 0xDA1A47U, 0x538BAEU, 0x389079U,
|
||||
0x216360U, 0x6A6B9FU, 0x30D84EU, 0x3903E1U, 0x421230U, 0x0BE94FU, 0x10FBD6U, 0x5B6011U, 0x4215E8U, 0x008EFBU,
|
||||
0xF95C07U, 0xF2759CU, 0xADE659U, 0xA49D22U, 0xFF0CB7U, 0x96964CU, 0x8CE591U, 0xC77E92U, 0x9E2F6FU, 0x959CBCU,
|
||||
0xCC0601U, 0xE7475AU, 0x3CFCBFU, 0x74EF25U, 0x6717F4U, 0x2E048BU, 0x15DF0AU, 0x5A6CF5U, 0x03716CU, 0x08A33BU,
|
||||
0x5098C2U, 0x5B1945U, 0x20C2BCU, 0x69F063U, 0xF22972U, 0xBB1A8DU, 0xE0814DU, 0xCA52F6U, 0x936AABU, 0x98B958U,
|
||||
0xC9A2C5U, 0x820306U, 0x9B593BU, 0xF4CAE0U, 0xEE7315U, 0xA7209EU, 0x7C9BCBU, 0x75C930U, 0x0E54A4U, 0x45E75FU,
|
||||
0x5CBC12U, 0x173D81U, 0x0F0778U, 0x44D4AFU, 0x3FC596U, 0x363E59U, 0x65ACE8U, 0x6C9737U, 0xA346E6U, 0xD95DD9U,
|
||||
0xD0FE10U, 0x8B26E6U, 0xC2357FU, 0xD9CE28U, 0x92DDC1U, 0xAB455EU, 0xE1B6AFU, 0xB8ADF4U, 0xB35861U, 0x68438AU,
|
||||
0x41C157U, 0x12B864U, 0x5B2BB9U, 0x45B00AU, 0x0EC3C6U, 0x175B95U, 0x7C2828U, 0x25B3F3U, 0x2E2206U, 0x75580DU,
|
||||
0x3DDBD8U, 0x86C223U, 0xCF31BAU, 0xD4AADDU, 0x9FF804U, 0xC64193U, 0xCD526AU, 0xBDA9F4U, 0xB2BD95U, 0xE92E4AU,
|
||||
0xA0C5FBU, 0xBBD424U, 0xD20F7DU, 0x093DDAU, 0x01A603U, 0x5A77F0U, 0x514CEDU, 0x089E36U, 0x63A7D3U, 0x7A3448U,
|
||||
0x31FF3CU, 0x2BCEE7U, 0x625462U, 0x150799U, 0x1CBC84U, 0xC7ED77U, 0x8C76AAU, 0x95C409U, 0xDF9954U, 0xE62A8FU,
|
||||
0xAD213EU, 0xF6F1E1U, 0xFF4A00U, 0xA4191EU, 0x8D82C7U, 0xD7B330U, 0x9C69A9U, 0x057ACEU, 0x42C357U, 0x2B1088U,
|
||||
0x303A79U, 0x7BEB76U, 0x63F087U, 0x284358U, 0x7198E1U, 0x5A88B2U, 0x017B7EU, 0x0860CDU, 0x53F510U, 0x9A0F63U,
|
||||
0xA00CFEU, 0xEBB535U, 0xF2E6C0U, 0xB97DDBU, 0xEC0F2EU, 0xE796B5U, 0x9C0578U, 0xD47E0BU, 0xCFEF92U, 0x86F564U,
|
||||
0x1D16ADU, 0x768F32U, 0x2FFC43U, 0x24669CU, 0x7C752DU, 0x778CE2U, 0x2E1F9BU, 0x45000CU, 0x5EF0D5U, 0x13FB62U,
|
||||
0x40282BU, 0x4811D4U, 0xB38344U, 0xB8789FU, 0xE16BEAU, 0xAAB261U, 0xB38194U, 0xF81B4FU, 0xC2CA52U, 0x8BE1B1U,
|
||||
0xD0726CU, 0xD903DFU, 0x829982U, 0xE94A79U, 0x7471FDU, 0x36E026U, 0x2FBA93U, 0x640DC8U, 0x3F1431U, 0x16D7B6U,
|
||||
0x4D6C6FU, 0x443C18U, 0x1E8781U, 0x55947EU, 0x6C4FBFU, 0x27FFA0U, 0xBEE451U, 0xF5378FU, 0xAE0E2EU, 0xA2CD71U,
|
||||
0xC9D7C8U, 0x98661FU, 0x93BDC6U, 0xC88EE5U, 0xC15438U, 0xBA45C3U, 0xF2FE56U, 0xE9290DU, 0x2230E8U, 0x3B9273U,
|
||||
0x70C98FU, 0x0958DCU, 0x02A343U, 0x58B0A2U, 0x150A7DU, 0x0E5BC4U, 0x6FC897U, 0x74F33AU, 0x3F23E9U, 0x66A834U,
|
||||
0xECDB0FU, 0xB542DAU, 0x9E5131U, 0xC7ABA5U, 0x8C38FEU, 0x97010BU, 0xDED290U, 0xA4CC7DU, 0xAD3D2EU, 0xF6B6B3U,
|
||||
0xF9A540U, 0x205ED9U, 0x634EB6U, 0x5A9567U, 0x11A6D8U, 0x0B3F09U};
|
||||
|
||||
CAMBEFEC::CAMBEFEC()
|
||||
{
|
||||
}
|
||||
|
||||
CAMBEFEC::~CAMBEFEC()
|
||||
{
|
||||
}
|
||||
|
||||
unsigned int CAMBEFEC::regenerate(unsigned char* bytes) const
|
||||
{
|
||||
wxASSERT(bytes != NULL);
|
||||
|
||||
unsigned int a = ((bytes[0] & 0x80) ? 0x800000 : 0x000000) | ((bytes[0] & 0x02) ? 0x400000 : 0x000000) |
|
||||
((bytes[1] & 0x08) ? 0x200000 : 0x000000) | ((bytes[2] & 0x20) ? 0x100000 : 0x000000) |
|
||||
((bytes[3] & 0x80) ? 0x080000 : 0x000000) | ((bytes[3] & 0x02) ? 0x040000 : 0x000000) |
|
||||
((bytes[4] & 0x08) ? 0x020000 : 0x000000) | ((bytes[5] & 0x20) ? 0x010000 : 0x000000) |
|
||||
((bytes[6] & 0x80) ? 0x008000 : 0x000000) | ((bytes[6] & 0x02) ? 0x004000 : 0x000000) |
|
||||
((bytes[7] & 0x08) ? 0x002000 : 0x000000) | ((bytes[8] & 0x20) ? 0x001000 : 0x000000) |
|
||||
((bytes[0] & 0x40) ? 0x000800 : 0x000000) | ((bytes[0] & 0x01) ? 0x000400 : 0x000000) |
|
||||
((bytes[1] & 0x04) ? 0x000200 : 0x000000) | ((bytes[2] & 0x10) ? 0x000100 : 0x000000) |
|
||||
((bytes[3] & 0x40) ? 0x000080 : 0x000000) | ((bytes[3] & 0x01) ? 0x000040 : 0x000000) |
|
||||
((bytes[4] & 0x04) ? 0x000020 : 0x000000) | ((bytes[5] & 0x10) ? 0x000010 : 0x000000) |
|
||||
((bytes[6] & 0x40) ? 0x000008 : 0x000000) | ((bytes[6] & 0x01) ? 0x000004 : 0x000000) |
|
||||
((bytes[7] & 0x04) ? 0x000002 : 0x000000) | ((bytes[8] & 0x10) ? 0x000001 : 0x000000);
|
||||
|
||||
unsigned int b = ((bytes[0] & 0x20) ? 0x800000 : 0x000000) | ((bytes[1] & 0x80) ? 0x400000 : 0x000000) |
|
||||
((bytes[1] & 0x02) ? 0x200000 : 0x000000) | ((bytes[2] & 0x08) ? 0x100000 : 0x000000) |
|
||||
((bytes[3] & 0x20) ? 0x080000 : 0x000000) | ((bytes[4] & 0x80) ? 0x040000 : 0x000000) |
|
||||
((bytes[4] & 0x02) ? 0x020000 : 0x000000) | ((bytes[5] & 0x08) ? 0x010000 : 0x000000) |
|
||||
((bytes[6] & 0x20) ? 0x008000 : 0x000000) | ((bytes[7] & 0x80) ? 0x004000 : 0x000000) |
|
||||
((bytes[7] & 0x02) ? 0x002000 : 0x000000) | ((bytes[8] & 0x08) ? 0x001000 : 0x000000) |
|
||||
((bytes[0] & 0x10) ? 0x000800 : 0x000000) | ((bytes[1] & 0x40) ? 0x000400 : 0x000000) |
|
||||
((bytes[1] & 0x01) ? 0x000200 : 0x000000) | ((bytes[2] & 0x04) ? 0x000100 : 0x000000) |
|
||||
((bytes[3] & 0x10) ? 0x000080 : 0x000000) | ((bytes[4] & 0x40) ? 0x000040 : 0x000000) |
|
||||
((bytes[4] & 0x01) ? 0x000020 : 0x000000) | ((bytes[5] & 0x04) ? 0x000010 : 0x000000) |
|
||||
((bytes[6] & 0x10) ? 0x000008 : 0x000000) | ((bytes[7] & 0x40) ? 0x000004 : 0x000000) |
|
||||
((bytes[7] & 0x01) ? 0x000002 : 0x000000) | ((bytes[8] & 0x04) ? 0x000001 : 0x000000);
|
||||
|
||||
unsigned int c = ((bytes[0] & 0x08) ? 0x800000 : 0x000000) | ((bytes[1] & 0x20) ? 0x400000 : 0x000000) |
|
||||
((bytes[2] & 0x80) ? 0x200000 : 0x000000) | ((bytes[2] & 0x02) ? 0x100000 : 0x000000) |
|
||||
((bytes[3] & 0x08) ? 0x080000 : 0x000000) | ((bytes[4] & 0x20) ? 0x040000 : 0x000000) |
|
||||
((bytes[5] & 0x80) ? 0x020000 : 0x000000) | ((bytes[5] & 0x02) ? 0x010000 : 0x000000) |
|
||||
((bytes[6] & 0x08) ? 0x008000 : 0x000000) | ((bytes[7] & 0x20) ? 0x004000 : 0x000000) |
|
||||
((bytes[8] & 0x80) ? 0x002000 : 0x000000) | ((bytes[8] & 0x02) ? 0x001000 : 0x000000) |
|
||||
((bytes[0] & 0x04) ? 0x000800 : 0x000000) | ((bytes[1] & 0x10) ? 0x000400 : 0x000000) |
|
||||
((bytes[2] & 0x40) ? 0x000200 : 0x000000) | ((bytes[2] & 0x01) ? 0x000100 : 0x000000) |
|
||||
((bytes[3] & 0x04) ? 0x000080 : 0x000000) | ((bytes[4] & 0x10) ? 0x000040 : 0x000000) |
|
||||
((bytes[5] & 0x40) ? 0x000020 : 0x000000) | ((bytes[5] & 0x01) ? 0x000010 : 0x000000) |
|
||||
((bytes[6] & 0x04) ? 0x000008 : 0x000000) | ((bytes[7] & 0x10) ? 0x000004 : 0x000000) |
|
||||
((bytes[8] & 0x40) ? 0x000002 : 0x000000) | ((bytes[8] & 0x01) ? 0x000001 : 0x000000);
|
||||
|
||||
unsigned int errors = regenerate(a, b, c);
|
||||
|
||||
bytes[0] = ((a & 0x800000) ? 0x80 : 0x00) | ((a & 0x000800) ? 0x40 : 0x00) | ((b & 0x800000) ? 0x20 : 0x00) |
|
||||
((b & 0x000800) ? 0x10 : 0x00) | ((c & 0x800000) ? 0x08 : 0x00) | ((c & 0x000800) ? 0x04 : 0x00) |
|
||||
((a & 0x400000) ? 0x02 : 0x00) | ((a & 0x000400) ? 0x01 : 0x00);
|
||||
|
||||
bytes[1] = ((b & 0x400000) ? 0x80 : 0x00) | ((b & 0x000400) ? 0x40 : 0x00) | ((c & 0x400000) ? 0x20 : 0x00) |
|
||||
((c & 0x000400) ? 0x10 : 0x00) | ((a & 0x200000) ? 0x08 : 0x00) | ((a & 0x000200) ? 0x04 : 0x00) |
|
||||
((b & 0x200000) ? 0x02 : 0x00) | ((b & 0x000200) ? 0x01 : 0x00);
|
||||
|
||||
bytes[2] = ((c & 0x200000) ? 0x80 : 0x00) | ((c & 0x000200) ? 0x40 : 0x00) | ((a & 0x100000) ? 0x20 : 0x00) |
|
||||
((a & 0x000100) ? 0x10 : 0x00) | ((b & 0x100000) ? 0x08 : 0x00) | ((b & 0x000100) ? 0x04 : 0x00) |
|
||||
((c & 0x100000) ? 0x02 : 0x00) | ((c & 0x000100) ? 0x01 : 0x00);
|
||||
|
||||
bytes[3] = ((a & 0x080000) ? 0x80 : 0x00) | ((a & 0x000080) ? 0x40 : 0x00) | ((b & 0x080000) ? 0x20 : 0x00) |
|
||||
((b & 0x000080) ? 0x10 : 0x00) | ((c & 0x080000) ? 0x08 : 0x00) | ((c & 0x000080) ? 0x04 : 0x00) |
|
||||
((a & 0x040000) ? 0x02 : 0x00) | ((a & 0x000040) ? 0x01 : 0x00);
|
||||
|
||||
bytes[4] = ((b & 0x040000) ? 0x80 : 0x00) | ((b & 0x000040) ? 0x40 : 0x00) | ((c & 0x040000) ? 0x20 : 0x00) |
|
||||
((c & 0x000040) ? 0x10 : 0x00) | ((a & 0x020000) ? 0x08 : 0x00) | ((a & 0x000020) ? 0x04 : 0x00) |
|
||||
((b & 0x020000) ? 0x02 : 0x00) | ((b & 0x000020) ? 0x01 : 0x00);
|
||||
|
||||
bytes[5] = ((c & 0x020000) ? 0x80 : 0x00) | ((c & 0x000020) ? 0x40 : 0x00) | ((a & 0x010000) ? 0x20 : 0x00) |
|
||||
((a & 0x000010) ? 0x10 : 0x00) | ((b & 0x010000) ? 0x08 : 0x00) | ((b & 0x000010) ? 0x04 : 0x00) |
|
||||
((c & 0x010000) ? 0x02 : 0x00) | ((c & 0x000010) ? 0x01 : 0x00);
|
||||
|
||||
bytes[6] = ((a & 0x008000) ? 0x80 : 0x00) | ((a & 0x000008) ? 0x40 : 0x00) | ((b & 0x008000) ? 0x20 : 0x00) |
|
||||
((b & 0x000008) ? 0x10 : 0x00) | ((c & 0x008000) ? 0x08 : 0x00) | ((c & 0x000008) ? 0x04 : 0x00) |
|
||||
((a & 0x004000) ? 0x02 : 0x00) | ((a & 0x000004) ? 0x01 : 0x00);
|
||||
|
||||
bytes[7] = ((b & 0x004000) ? 0x80 : 0x00) | ((b & 0x000004) ? 0x40 : 0x00) | ((c & 0x004000) ? 0x20 : 0x00) |
|
||||
((c & 0x000004) ? 0x10 : 0x00) | ((a & 0x002000) ? 0x08 : 0x00) | ((a & 0x000002) ? 0x04 : 0x00) |
|
||||
((b & 0x002000) ? 0x02 : 0x00) | ((b & 0x000002) ? 0x01 : 0x00);
|
||||
|
||||
bytes[8] = ((c & 0x002000) ? 0x80 : 0x00) | ((c & 0x000002) ? 0x40 : 0x00) | ((a & 0x001000) ? 0x20 : 0x00) |
|
||||
((a & 0x000001) ? 0x10 : 0x00) | ((b & 0x001000) ? 0x08 : 0x00) | ((b & 0x000001) ? 0x04 : 0x00) |
|
||||
((c & 0x001000) ? 0x02 : 0x00) | ((c & 0x000001) ? 0x01 : 0x00);
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
unsigned int CAMBEFEC::count(const unsigned char* bytes) const
|
||||
{
|
||||
wxASSERT(bytes != NULL);
|
||||
|
||||
unsigned int a = ((bytes[0] & 0x80) ? 0x800000 : 0x000000) | ((bytes[0] & 0x02) ? 0x400000 : 0x000000) |
|
||||
((bytes[1] & 0x08) ? 0x200000 : 0x000000) | ((bytes[2] & 0x20) ? 0x100000 : 0x000000) |
|
||||
((bytes[3] & 0x80) ? 0x080000 : 0x000000) | ((bytes[3] & 0x02) ? 0x040000 : 0x000000) |
|
||||
((bytes[4] & 0x08) ? 0x020000 : 0x000000) | ((bytes[5] & 0x20) ? 0x010000 : 0x000000) |
|
||||
((bytes[6] & 0x80) ? 0x008000 : 0x000000) | ((bytes[6] & 0x02) ? 0x004000 : 0x000000) |
|
||||
((bytes[7] & 0x08) ? 0x002000 : 0x000000) | ((bytes[8] & 0x20) ? 0x001000 : 0x000000) |
|
||||
((bytes[0] & 0x40) ? 0x000800 : 0x000000) | ((bytes[0] & 0x01) ? 0x000400 : 0x000000) |
|
||||
((bytes[1] & 0x04) ? 0x000200 : 0x000000) | ((bytes[2] & 0x10) ? 0x000100 : 0x000000) |
|
||||
((bytes[3] & 0x40) ? 0x000080 : 0x000000) | ((bytes[3] & 0x01) ? 0x000040 : 0x000000) |
|
||||
((bytes[4] & 0x04) ? 0x000020 : 0x000000) | ((bytes[5] & 0x10) ? 0x000010 : 0x000000) |
|
||||
((bytes[6] & 0x40) ? 0x000008 : 0x000000) | ((bytes[6] & 0x01) ? 0x000004 : 0x000000) |
|
||||
((bytes[7] & 0x04) ? 0x000002 : 0x000000) | ((bytes[8] & 0x10) ? 0x000001 : 0x000000);
|
||||
|
||||
unsigned int b = ((bytes[0] & 0x20) ? 0x800000 : 0x000000) | ((bytes[1] & 0x80) ? 0x400000 : 0x000000) |
|
||||
((bytes[1] & 0x02) ? 0x200000 : 0x000000) | ((bytes[2] & 0x08) ? 0x100000 : 0x000000) |
|
||||
((bytes[3] & 0x20) ? 0x080000 : 0x000000) | ((bytes[4] & 0x80) ? 0x040000 : 0x000000) |
|
||||
((bytes[4] & 0x02) ? 0x020000 : 0x000000) | ((bytes[5] & 0x08) ? 0x010000 : 0x000000) |
|
||||
((bytes[6] & 0x20) ? 0x008000 : 0x000000) | ((bytes[7] & 0x80) ? 0x004000 : 0x000000) |
|
||||
((bytes[7] & 0x02) ? 0x002000 : 0x000000) | ((bytes[8] & 0x08) ? 0x001000 : 0x000000) |
|
||||
((bytes[0] & 0x10) ? 0x000800 : 0x000000) | ((bytes[1] & 0x40) ? 0x000400 : 0x000000) |
|
||||
((bytes[1] & 0x01) ? 0x000200 : 0x000000) | ((bytes[2] & 0x04) ? 0x000100 : 0x000000) |
|
||||
((bytes[3] & 0x10) ? 0x000080 : 0x000000) | ((bytes[4] & 0x40) ? 0x000040 : 0x000000) |
|
||||
((bytes[4] & 0x01) ? 0x000020 : 0x000000) | ((bytes[5] & 0x04) ? 0x000010 : 0x000000) |
|
||||
((bytes[6] & 0x10) ? 0x000008 : 0x000000) | ((bytes[7] & 0x40) ? 0x000004 : 0x000000) |
|
||||
((bytes[7] & 0x01) ? 0x000002 : 0x000000) | ((bytes[8] & 0x04) ? 0x000001 : 0x000000);
|
||||
|
||||
unsigned int c = ((bytes[0] & 0x08) ? 0x800000 : 0x000000) | ((bytes[1] & 0x20) ? 0x400000 : 0x000000) |
|
||||
((bytes[2] & 0x80) ? 0x200000 : 0x000000) | ((bytes[2] & 0x02) ? 0x100000 : 0x000000) |
|
||||
((bytes[3] & 0x08) ? 0x080000 : 0x000000) | ((bytes[4] & 0x20) ? 0x040000 : 0x000000) |
|
||||
((bytes[5] & 0x80) ? 0x020000 : 0x000000) | ((bytes[5] & 0x02) ? 0x010000 : 0x000000) |
|
||||
((bytes[6] & 0x08) ? 0x008000 : 0x000000) | ((bytes[7] & 0x20) ? 0x004000 : 0x000000) |
|
||||
((bytes[8] & 0x80) ? 0x002000 : 0x000000) | ((bytes[8] & 0x02) ? 0x001000 : 0x000000) |
|
||||
((bytes[0] & 0x04) ? 0x000800 : 0x000000) | ((bytes[1] & 0x10) ? 0x000400 : 0x000000) |
|
||||
((bytes[2] & 0x40) ? 0x000200 : 0x000000) | ((bytes[2] & 0x01) ? 0x000100 : 0x000000) |
|
||||
((bytes[3] & 0x04) ? 0x000080 : 0x000000) | ((bytes[4] & 0x10) ? 0x000040 : 0x000000) |
|
||||
((bytes[5] & 0x40) ? 0x000020 : 0x000000) | ((bytes[5] & 0x01) ? 0x000010 : 0x000000) |
|
||||
((bytes[6] & 0x04) ? 0x000008 : 0x000000) | ((bytes[7] & 0x10) ? 0x000004 : 0x000000) |
|
||||
((bytes[8] & 0x40) ? 0x000002 : 0x000000) | ((bytes[8] & 0x01) ? 0x000001 : 0x000000);
|
||||
|
||||
return count(a, b, c);
|
||||
}
|
||||
|
||||
unsigned int CAMBEFEC::regenerate(unsigned int& a, unsigned int& b, unsigned int&) const
|
||||
{
|
||||
unsigned int old_a = a;
|
||||
unsigned int old_b = b;
|
||||
|
||||
unsigned int data = CGolay::decode24128(a);
|
||||
|
||||
unsigned int new_a = CGolay::encode24128(data);
|
||||
|
||||
// The PRNG
|
||||
unsigned int p = PRNG_TABLE[data];
|
||||
|
||||
b ^= p;
|
||||
|
||||
unsigned int datb = CGolay::decode24128(b);
|
||||
|
||||
unsigned int new_b = CGolay::encode24128(datb);
|
||||
|
||||
new_b ^= p;
|
||||
|
||||
unsigned int errors = 0U;
|
||||
unsigned int mask = 0x01;
|
||||
for (unsigned int i = 0U; i < 24U; i++, mask <<= 1) {
|
||||
if ((old_a & mask) != (new_a & mask))
|
||||
errors++;
|
||||
if ((old_b & mask) != (new_b & mask))
|
||||
errors++;
|
||||
}
|
||||
|
||||
a = new_a;
|
||||
b = new_b;
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
unsigned int CAMBEFEC::count(unsigned int a, unsigned int b, unsigned int) const
|
||||
{
|
||||
unsigned int old_a = a;
|
||||
unsigned int old_b = b;
|
||||
|
||||
unsigned int data = CGolay::decode24128(a);
|
||||
|
||||
unsigned int new_a = CGolay::encode24128(data);
|
||||
|
||||
// The PRNG
|
||||
unsigned int p = PRNG_TABLE[data];
|
||||
|
||||
b ^= p;
|
||||
|
||||
unsigned int datb = CGolay::decode24128(b);
|
||||
|
||||
unsigned int new_b = CGolay::encode24128(datb);
|
||||
|
||||
new_b ^= p;
|
||||
|
||||
unsigned int errors = 0U;
|
||||
unsigned int mask = 0x01;
|
||||
for (unsigned int i = 0U; i < 24U; i++, mask <<= 1) {
|
||||
if ((old_a & mask) != (new_a & mask))
|
||||
errors++;
|
||||
if ((old_b & mask) != (new_b & mask))
|
||||
errors++;
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 2010,2014 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef AMBEFEC_H
|
||||
#define AMBEFEC_H
|
||||
|
||||
#include "Golay.h"
|
||||
|
||||
class CAMBEFEC {
|
||||
public:
|
||||
CAMBEFEC();
|
||||
~CAMBEFEC();
|
||||
|
||||
unsigned int regenerate(unsigned char* bytes) const;
|
||||
unsigned int count(const unsigned char* bytes) const;
|
||||
|
||||
private:
|
||||
unsigned int regenerate(unsigned int& a, unsigned int& b, unsigned int& c) const;
|
||||
unsigned int count(unsigned int a, unsigned int b, unsigned int c) const;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (C) 2013 by Jonathan Naylor, G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef AnnouncementCallback_H
|
||||
#define AnnouncementCallback_H
|
||||
|
||||
#include "HeaderData.h"
|
||||
|
||||
class IAnnouncementCallback {
|
||||
public:
|
||||
virtual void transmitAnnouncementHeader(CHeaderData* header) = 0;
|
||||
|
||||
virtual void transmitAnnouncementData(const unsigned char* data, unsigned int length, bool end) = 0;
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,149 @@
|
||||
/*
|
||||
* Copyright (C) 2013 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "AnnouncementUnit.h"
|
||||
|
||||
#include <wx/filename.h>
|
||||
#include <wx/file.h>
|
||||
|
||||
const wxString GLOBAL_FILE_NAME = wxT("Announce");
|
||||
|
||||
CAnnouncementUnit::CAnnouncementUnit(IAnnouncementCallback* handler, const wxString& callsign) :
|
||||
m_handler(handler),
|
||||
m_localFileName(),
|
||||
m_reader(),
|
||||
m_writer(),
|
||||
m_time(),
|
||||
m_out(0U),
|
||||
m_sending(false)
|
||||
{
|
||||
wxASSERT(handler != NULL);
|
||||
|
||||
m_localFileName.Printf(wxT("Announce %s"), callsign.c_str());
|
||||
#if !defined(__WINDOWS__)
|
||||
m_localFileName.Replace(wxT(" "), wxT("_"));
|
||||
#endif
|
||||
|
||||
m_writer.setDirectory(wxFileName::GetHomeDir());
|
||||
}
|
||||
|
||||
CAnnouncementUnit::~CAnnouncementUnit()
|
||||
{
|
||||
}
|
||||
|
||||
bool CAnnouncementUnit::writeHeader(const CHeaderData& header)
|
||||
{
|
||||
return m_writer.open(m_localFileName, header);
|
||||
}
|
||||
|
||||
bool CAnnouncementUnit::writeData(const unsigned char* data, unsigned int length, bool end)
|
||||
{
|
||||
bool ret = m_writer.write(data, length);
|
||||
if (!ret)
|
||||
return false;
|
||||
|
||||
if (end)
|
||||
m_writer.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CAnnouncementUnit::deleteAnnouncement()
|
||||
{
|
||||
wxFileName fileName(wxFileName::GetHomeDir(), m_localFileName, wxT("dvtool"));
|
||||
|
||||
if (wxFile::Exists(fileName.GetFullPath()))
|
||||
::wxRemoveFile(fileName.GetFullPath());
|
||||
}
|
||||
|
||||
void CAnnouncementUnit::startAnnouncement()
|
||||
{
|
||||
wxFileName fileName1(wxFileName::GetHomeDir(), m_localFileName, wxT("dvtool"));
|
||||
wxFileName fileName2(wxFileName::GetHomeDir(), GLOBAL_FILE_NAME, wxT("dvtool"));
|
||||
|
||||
if (wxFile::Exists(fileName1.GetFullPath())) {
|
||||
bool ret = m_reader.open(fileName1.GetFullPath());
|
||||
if (!ret)
|
||||
return;
|
||||
} else if (wxFile::Exists(fileName2.GetFullPath())) {
|
||||
bool ret = m_reader.open(fileName2.GetFullPath());
|
||||
if (!ret)
|
||||
return;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
DVTFR_TYPE type = m_reader.read();
|
||||
if (type != DVTFR_HEADER) {
|
||||
wxLogError(wxT("Invalid header element in the file - %d"), int(type));
|
||||
m_reader.close();
|
||||
return;
|
||||
}
|
||||
|
||||
CHeaderData* header = m_reader.readHeader();
|
||||
if (header == NULL) {
|
||||
wxLogError(wxT("NULL header element in the file"));
|
||||
m_reader.close();
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove the repeater bit
|
||||
header->setFlag1(header->getFlag1() & ~REPEATER_MASK);
|
||||
|
||||
m_handler->transmitAnnouncementHeader(header);
|
||||
|
||||
m_time.Start();
|
||||
|
||||
m_out = 0U;
|
||||
m_sending = true;
|
||||
}
|
||||
|
||||
void CAnnouncementUnit::clock()
|
||||
{
|
||||
if (!m_sending)
|
||||
return;
|
||||
|
||||
unsigned int needed = m_time.Time() / DSTAR_FRAME_TIME_MS;
|
||||
|
||||
while (m_out < needed) {
|
||||
DVTFR_TYPE type = m_reader.read();
|
||||
if (type != DVTFR_DATA) {
|
||||
wxLogError(wxT("Invalid data element in the file - %d"), int(type));
|
||||
m_handler->transmitAnnouncementData(END_PATTERN_BYTES, DV_FRAME_LENGTH_BYTES, true);
|
||||
m_reader.close();
|
||||
m_sending = false;
|
||||
m_out = 0U;
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned char data[DV_FRAME_MAX_LENGTH_BYTES];
|
||||
bool end;
|
||||
m_reader.readData(data, DV_FRAME_MAX_LENGTH_BYTES, end);
|
||||
|
||||
m_handler->transmitAnnouncementData(data, DV_FRAME_LENGTH_BYTES, false);
|
||||
m_out++;
|
||||
|
||||
if (end) {
|
||||
m_handler->transmitAnnouncementData(END_PATTERN_BYTES, DV_FRAME_LENGTH_BYTES, true);
|
||||
m_reader.close();
|
||||
m_sending = false;
|
||||
m_out = 0U;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) 2013 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef AnnouncementUnit_H
|
||||
#define AnnouncementUnit_H
|
||||
|
||||
#include "AnnouncementCallback.h"
|
||||
#include "DVTOOLFileWriter.h"
|
||||
#include "DVTOOLFileReader.h"
|
||||
#include "DStarDefines.h"
|
||||
#include "HeaderData.h"
|
||||
|
||||
#include <wx/wx.h>
|
||||
|
||||
class CAnnouncementUnit {
|
||||
public:
|
||||
CAnnouncementUnit(IAnnouncementCallback* handler, const wxString& callsign);
|
||||
~CAnnouncementUnit();
|
||||
|
||||
bool writeHeader(const CHeaderData& header);
|
||||
bool writeData(const unsigned char* data, unsigned int length, bool end);
|
||||
|
||||
void deleteAnnouncement();
|
||||
|
||||
void startAnnouncement();
|
||||
|
||||
void clock();
|
||||
|
||||
private:
|
||||
IAnnouncementCallback* m_handler;
|
||||
wxString m_localFileName;
|
||||
CDVTOOLFileReader m_reader;
|
||||
CDVTOOLFileWriter m_writer;
|
||||
wxStopWatch m_time;
|
||||
unsigned int m_out;
|
||||
bool m_sending;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright (C) 2013 by Jonathan Naylor, G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "ArduinoController.h"
|
||||
|
||||
|
||||
const char IN_PORT1 = 0x01U;
|
||||
const char IN_PORT2 = 0x02U;
|
||||
const char IN_PORT3 = 0x04U;
|
||||
const char IN_PORT4 = 0x08U;
|
||||
const char IN_PORT5 = 0x10U;
|
||||
|
||||
const char OUT_PORT1 = 0x01U;
|
||||
const char OUT_PORT2 = 0x02U;
|
||||
const char OUT_PORT3 = 0x04U;
|
||||
const char OUT_PORT4 = 0x08U;
|
||||
const char OUT_PORT5 = 0x10U;
|
||||
const char OUT_PORT6 = 0x20U;
|
||||
const char OUT_PORT7 = 0x40U;
|
||||
const char OUT_PORT8 = 0x80U;
|
||||
|
||||
|
||||
CArduinoController::CArduinoController(const wxString& port) :
|
||||
m_serial(port, SERIAL_19200),
|
||||
m_out(0x00U),
|
||||
m_in(0x00U)
|
||||
{
|
||||
}
|
||||
|
||||
CArduinoController::~CArduinoController()
|
||||
{
|
||||
}
|
||||
|
||||
bool CArduinoController::open()
|
||||
{
|
||||
bool ret = m_serial.open();
|
||||
if (!ret)
|
||||
return false;
|
||||
|
||||
unsigned char buffer;
|
||||
while (m_serial.read(&buffer, 1U) == 1)
|
||||
;
|
||||
|
||||
m_out = 0x00U;
|
||||
m_in = 0x00U;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CArduinoController::getDigitalInputs(bool& inp1, bool& inp2, bool& inp3, bool& inp4, bool& inp5)
|
||||
{
|
||||
unsigned char buffer;
|
||||
int ret = m_serial.read(&buffer, 1U);
|
||||
if (ret == 1) {
|
||||
m_in = buffer;
|
||||
m_serial.write(&m_out, 1U);
|
||||
}
|
||||
|
||||
inp1 = (m_in & IN_PORT1) == IN_PORT1;
|
||||
inp2 = (m_in & IN_PORT2) == IN_PORT2;
|
||||
inp3 = (m_in & IN_PORT3) == IN_PORT3;
|
||||
inp4 = (m_in & IN_PORT4) == IN_PORT4;
|
||||
inp5 = (m_in & IN_PORT5) == IN_PORT5;
|
||||
}
|
||||
|
||||
void CArduinoController::setDigitalOutputs(bool outp1, bool outp2, bool outp3, bool outp4, bool outp5, bool outp6, bool outp7, bool outp8)
|
||||
{
|
||||
m_out = 0x00U;
|
||||
m_out |= outp1 ? OUT_PORT1 : 0x00U;
|
||||
m_out |= outp2 ? OUT_PORT2 : 0x00U;
|
||||
m_out |= outp3 ? OUT_PORT3 : 0x00U;
|
||||
m_out |= outp4 ? OUT_PORT4 : 0x00U;
|
||||
m_out |= outp5 ? OUT_PORT5 : 0x00U;
|
||||
m_out |= outp6 ? OUT_PORT6 : 0x00U;
|
||||
m_out |= outp7 ? OUT_PORT7 : 0x00U;
|
||||
m_out |= outp8 ? OUT_PORT8 : 0x00U;
|
||||
|
||||
unsigned char buffer;
|
||||
int ret = m_serial.read(&buffer, 1U);
|
||||
if (ret == 1) {
|
||||
m_in = buffer;
|
||||
m_serial.write(&m_out, 1U);
|
||||
}
|
||||
}
|
||||
|
||||
void CArduinoController::close()
|
||||
{
|
||||
unsigned char buffer = 0x00U;
|
||||
m_serial.write(&buffer, 1U);
|
||||
|
||||
m_serial.close();
|
||||
}
|
||||
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2013 by Jonathan Naylor, G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef ArduinoController_H
|
||||
#define ArduinoController_H
|
||||
|
||||
#include "SerialDataController.h"
|
||||
#include "HardwareController.h"
|
||||
|
||||
#include <wx/wx.h>
|
||||
|
||||
|
||||
class CArduinoController : public IHardwareController {
|
||||
public:
|
||||
CArduinoController(const wxString& port);
|
||||
virtual ~CArduinoController();
|
||||
|
||||
virtual bool open();
|
||||
|
||||
virtual void getDigitalInputs(bool& inp1, bool& inp2, bool& inp3, bool& inp4, bool& inp5);
|
||||
|
||||
virtual void setDigitalOutputs(bool outp1, bool outp2, bool outp3, bool outp4, bool outp5, bool outp6, bool outp7, bool outp8);
|
||||
|
||||
virtual void close();
|
||||
|
||||
private:
|
||||
CSerialDataController m_serial;
|
||||
unsigned char m_out;
|
||||
unsigned char m_in;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2009,2015 by Jonathan Naylor, G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef AudioCallback_H
|
||||
#define AudioCallback_H
|
||||
|
||||
#include <wx/wx.h>
|
||||
|
||||
class IAudioCallback {
|
||||
public:
|
||||
virtual void readCallback(const wxFloat32* input, unsigned int nSamples, int id) = 0;
|
||||
virtual void writeCallback(wxFloat32* output, unsigned int& nSamples, int id) = 0;
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (C) 2012 by Jonathan Naylor, G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef BeaconCallback_H
|
||||
#define BeaconCallback_H
|
||||
|
||||
class IBeaconCallback {
|
||||
public:
|
||||
virtual void transmitBeaconHeader() = 0;
|
||||
|
||||
virtual void transmitBeaconData(const unsigned char* data, unsigned int length, bool end) = 0;
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,394 @@
|
||||
/*
|
||||
* Copyright (C) 2011,2012 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "BeaconUnit.h"
|
||||
|
||||
#include <wx/filename.h>
|
||||
#include <wx/textfile.h>
|
||||
#include <wx/tokenzr.h>
|
||||
#include <wx/ffile.h>
|
||||
|
||||
const unsigned int MAX_FRAMES = 60U * DSTAR_FRAMES_PER_SEC;
|
||||
|
||||
const unsigned int SILENCE_LENGTH = 10U;
|
||||
|
||||
CBeaconUnit::CBeaconUnit(IBeaconCallback* handler, const wxString& callsign, const wxString& text, bool voice, TEXT_LANG language) :
|
||||
m_ambe(NULL),
|
||||
m_ambeLength(0U),
|
||||
m_data(NULL),
|
||||
m_dataLength(0U),
|
||||
m_index(),
|
||||
m_language(language),
|
||||
m_handler(handler),
|
||||
m_callsign(callsign),
|
||||
m_encoder(),
|
||||
m_in(0U),
|
||||
m_out(0U),
|
||||
m_seqNo(0U),
|
||||
m_time(),
|
||||
m_sending(false)
|
||||
{
|
||||
wxASSERT(handler != NULL);
|
||||
|
||||
wxString slowData = text;
|
||||
slowData.resize(20U, wxT(' '));
|
||||
m_encoder.setTextData(slowData);
|
||||
|
||||
m_data = new unsigned char[MAX_FRAMES * VOICE_FRAME_LENGTH_BYTES];
|
||||
::memset(m_data, 0x00U, MAX_FRAMES * VOICE_FRAME_LENGTH_BYTES);
|
||||
|
||||
if (!voice)
|
||||
return;
|
||||
|
||||
wxString ambeFileName;
|
||||
wxString indxFileName;
|
||||
|
||||
switch (m_language) {
|
||||
case TL_DEUTSCH:
|
||||
ambeFileName = wxT("de_DE.ambe");
|
||||
indxFileName = wxT("de_DE.indx");
|
||||
break;
|
||||
case TL_DANSK:
|
||||
ambeFileName = wxT("dk_DK.ambe");
|
||||
indxFileName = wxT("dk_DK.indx");
|
||||
break;
|
||||
case TL_ITALIANO:
|
||||
ambeFileName = wxT("it_IT.ambe");
|
||||
indxFileName = wxT("it_IT.indx");
|
||||
break;
|
||||
case TL_FRANCAIS:
|
||||
ambeFileName = wxT("fr_FR.ambe");
|
||||
indxFileName = wxT("fr_FR.indx");
|
||||
break;
|
||||
case TL_ESPANOL:
|
||||
ambeFileName = wxT("es_ES.ambe");
|
||||
indxFileName = wxT("es_ES.indx");
|
||||
break;
|
||||
case TL_SVENSKA:
|
||||
ambeFileName = wxT("se_SE.ambe");
|
||||
indxFileName = wxT("se_SE.indx");
|
||||
break;
|
||||
case TL_POLSKI:
|
||||
ambeFileName = wxT("pl_PL.ambe");
|
||||
indxFileName = wxT("pl_PL.indx");
|
||||
break;
|
||||
case TL_ENGLISH_US:
|
||||
ambeFileName = wxT("en_US.ambe");
|
||||
indxFileName = wxT("en_US.indx");
|
||||
break;
|
||||
case TL_NORSK:
|
||||
ambeFileName = wxT("no_NO.ambe");
|
||||
indxFileName = wxT("no_NO.indx");
|
||||
break;
|
||||
// case TL_NEDERLANDS_NL:
|
||||
// ambeFileName = wxT("nl_NL.ambe");
|
||||
// indxFileName = wxT("nl_NL.indx");
|
||||
// break;
|
||||
// case TL_NEDERLANDS_BE:
|
||||
// ambeFileName = wxT("nl_BE.ambe");
|
||||
// indxFileName = wxT("nl_BE.indx");
|
||||
// break;
|
||||
default:
|
||||
ambeFileName = wxT("en_GB.ambe");
|
||||
indxFileName = wxT("en_GB.indx");
|
||||
break;
|
||||
}
|
||||
|
||||
bool ret = readAMBE(ambeFileName);
|
||||
if (!ret)
|
||||
return;
|
||||
|
||||
readIndex(indxFileName);
|
||||
}
|
||||
|
||||
CBeaconUnit::~CBeaconUnit()
|
||||
{
|
||||
for (CIndexList_t::iterator it = m_index.begin(); it != m_index.end(); ++it)
|
||||
delete it->second;
|
||||
|
||||
delete[] m_ambe;
|
||||
delete[] m_data;
|
||||
}
|
||||
|
||||
void CBeaconUnit::sendBeacon()
|
||||
{
|
||||
m_handler->transmitBeaconHeader();
|
||||
|
||||
m_sending = true;
|
||||
|
||||
m_time.Start();
|
||||
|
||||
m_in = 0U;
|
||||
m_out = 0U;
|
||||
m_seqNo = 0U;
|
||||
m_dataLength = 0U;
|
||||
|
||||
if (m_ambe == NULL) {
|
||||
for (unsigned int i = 0U; i < 21U; i++) {
|
||||
unsigned char buffer[DV_FRAME_LENGTH_BYTES];
|
||||
|
||||
if (i == 0U) {
|
||||
m_encoder.sync();
|
||||
|
||||
::memcpy(buffer + 0U, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES);
|
||||
::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, DATA_SYNC_BYTES, DATA_FRAME_LENGTH_BYTES);
|
||||
} else {
|
||||
unsigned char text[DATA_FRAME_LENGTH_BYTES];
|
||||
m_encoder.getTextData(text);
|
||||
|
||||
::memcpy(buffer + 0U, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES);
|
||||
::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, text, DATA_FRAME_LENGTH_BYTES);
|
||||
}
|
||||
|
||||
::memcpy(m_data + m_dataLength, buffer, DV_FRAME_LENGTH_BYTES);
|
||||
m_dataLength += DV_FRAME_LENGTH_BYTES;
|
||||
m_in++;
|
||||
}
|
||||
} else {
|
||||
lookup(wxT(" "));
|
||||
lookup(wxT(" "));
|
||||
lookup(wxT(" "));
|
||||
lookup(wxT(" "));
|
||||
|
||||
spellCallsign(m_callsign);
|
||||
|
||||
lookup(wxT(" "));
|
||||
lookup(wxT(" "));
|
||||
lookup(wxT(" "));
|
||||
lookup(wxT(" "));
|
||||
}
|
||||
}
|
||||
|
||||
void CBeaconUnit::clock()
|
||||
{
|
||||
if (!m_sending)
|
||||
return;
|
||||
|
||||
unsigned int needed = m_time.Time() / DSTAR_FRAME_TIME_MS;
|
||||
|
||||
while (m_out < needed) {
|
||||
m_handler->transmitBeaconData(m_data + m_out * DV_FRAME_LENGTH_BYTES, DV_FRAME_LENGTH_BYTES, false);
|
||||
m_out++;
|
||||
|
||||
if (m_in == m_out) {
|
||||
m_handler->transmitBeaconData(END_PATTERN_BYTES, DV_FRAME_LENGTH_BYTES, true);
|
||||
m_sending = false;
|
||||
m_in = 0U;
|
||||
m_out = 0U;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CBeaconUnit::lookup(const wxString &id)
|
||||
{
|
||||
CIndexRecord* info = m_index[id];
|
||||
if (info == NULL) {
|
||||
// wxLogError(wxT("Cannot find the AMBE index for *%s*"), id.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned int start = info->getStart();
|
||||
unsigned int length = info->getLength();
|
||||
|
||||
for (unsigned int i = 0U; i < length; i++) {
|
||||
unsigned char* dataIn = m_ambe + (start + i) * VOICE_FRAME_LENGTH_BYTES;
|
||||
|
||||
unsigned char buffer[DV_FRAME_LENGTH_BYTES];
|
||||
::memcpy(buffer + 0U, dataIn, VOICE_FRAME_LENGTH_BYTES);
|
||||
|
||||
// Insert sync bytes when the sequence number is zero, slow data otherwise
|
||||
if (m_seqNo == 0U) {
|
||||
::memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, DATA_SYNC_BYTES, DATA_FRAME_LENGTH_BYTES);
|
||||
m_encoder.sync();
|
||||
} else {
|
||||
m_encoder.getTextData(buffer + VOICE_FRAME_LENGTH_BYTES);
|
||||
}
|
||||
|
||||
::memcpy(m_data + m_dataLength, buffer, DV_FRAME_LENGTH_BYTES);
|
||||
m_dataLength += DV_FRAME_LENGTH_BYTES;
|
||||
m_in++;
|
||||
|
||||
m_seqNo++;
|
||||
if (m_seqNo == 21U)
|
||||
m_seqNo = 0U;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CBeaconUnit::spellCallsign(const wxString &callsign)
|
||||
{
|
||||
unsigned int length = callsign.Len();
|
||||
|
||||
for (unsigned int i = 0U; i < (length - 1U); i++) {
|
||||
wxString c = callsign.Mid(i, 1U);
|
||||
|
||||
if (!c.IsSameAs(wxT(" ")))
|
||||
lookup(c);
|
||||
}
|
||||
|
||||
wxChar c = callsign.GetChar(length - 1U);
|
||||
|
||||
switch (c) {
|
||||
case wxT('A'):
|
||||
lookup(wxT("alpha"));
|
||||
break;
|
||||
case wxT('B'):
|
||||
lookup(wxT("bravo"));
|
||||
break;
|
||||
case wxT('C'):
|
||||
lookup(wxT("charlie"));
|
||||
break;
|
||||
case wxT('D'):
|
||||
lookup(wxT("delta"));
|
||||
break;
|
||||
default:
|
||||
lookup(wxString(c));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool CBeaconUnit::readAMBE(const wxString& name)
|
||||
{
|
||||
wxFileName fileName(wxFileName::GetHomeDir(), name);
|
||||
|
||||
if (!fileName.IsFileReadable()) {
|
||||
wxLogMessage(wxT("File %s not readable"), fileName.GetFullPath().c_str());
|
||||
#if defined(__WINDOWS__)
|
||||
fileName.Assign(::wxGetCwd(), name);
|
||||
#else
|
||||
fileName.Assign(wxT(DATA_DIR), name);
|
||||
#endif
|
||||
if (!fileName.IsFileReadable()) {
|
||||
wxLogMessage(wxT("File %s not readable"), fileName.GetFullPath().c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
wxFFile file;
|
||||
|
||||
bool ret = file.Open(fileName.GetFullPath().c_str(), wxT("rb"));
|
||||
if (!ret) {
|
||||
wxLogMessage(wxT("Cannot open %s for reading"), fileName.GetFullPath().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
wxLogMessage(wxT("Reading %s"), fileName.GetFullPath().c_str());
|
||||
|
||||
unsigned char buffer[VOICE_FRAME_LENGTH_BYTES];
|
||||
|
||||
size_t n = file.Read(buffer, 4U);
|
||||
if (n != 4U) {
|
||||
wxLogMessage(wxT("Unable to read the header from %s"), fileName.GetFullPath().c_str());
|
||||
file.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (::memcmp(buffer, "AMBE", 4U) != 0) {
|
||||
wxLogMessage(wxT("Invalid header from %s"), fileName.GetFullPath().c_str());
|
||||
file.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Length of the file minus the header
|
||||
unsigned int length = file.Length() - 4U;
|
||||
|
||||
// Hold the file data plus silence at the end
|
||||
m_ambe = new unsigned char[length + SILENCE_LENGTH * VOICE_FRAME_LENGTH_BYTES];
|
||||
m_ambeLength = length / VOICE_FRAME_LENGTH_BYTES;
|
||||
|
||||
// Add silence to the beginning of the buffer
|
||||
unsigned char* p = m_ambe;
|
||||
for (unsigned int i = 0U; i < SILENCE_LENGTH; i++, p += VOICE_FRAME_LENGTH_BYTES)
|
||||
::memcpy(p, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES);
|
||||
|
||||
n = file.Read(p, length);
|
||||
if (n != length) {
|
||||
wxLogMessage(wxT("Unable to read the AMBE data from %s"), fileName.GetFullPath().c_str());
|
||||
file.Close();
|
||||
delete[] m_ambe;
|
||||
m_ambe = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
file.Close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBeaconUnit::readIndex(const wxString& name)
|
||||
{
|
||||
wxFileName fileName(wxFileName::GetHomeDir(), name);
|
||||
|
||||
if (!fileName.IsFileReadable()) {
|
||||
wxLogMessage(wxT("File %s not readable"), fileName.GetFullPath().c_str());
|
||||
#if defined(__WINDOWS__)
|
||||
fileName.Assign(::wxGetCwd(), name);
|
||||
#else
|
||||
fileName.Assign(wxT(DATA_DIR), name);
|
||||
#endif
|
||||
if (!fileName.IsFileReadable()) {
|
||||
wxLogMessage(wxT("File %s not readable"), fileName.GetFullPath().c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
wxTextFile file;
|
||||
|
||||
bool ret = file.Open(fileName.GetFullPath());
|
||||
if (!ret) {
|
||||
wxLogMessage(wxT("Cannot open %s for reading"), fileName.GetFullPath().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add a silence entry at the beginning
|
||||
m_index[wxT(" ")] = new CIndexRecord(wxT(" "), 0U, SILENCE_LENGTH);
|
||||
|
||||
wxLogMessage(wxT("Reading %s"), fileName.GetFullPath().c_str());
|
||||
|
||||
unsigned int nLines = file.GetLineCount();
|
||||
|
||||
for (unsigned int i = 0; i < nLines; i++) {
|
||||
wxString line = file.GetLine(i);
|
||||
|
||||
if (line.length() > 0 && line.GetChar(0) != wxT('#')) {
|
||||
wxStringTokenizer t(line, wxT(" \t\r\n"), wxTOKEN_STRTOK);
|
||||
wxString name = t.GetNextToken();
|
||||
wxString startTxt = t.GetNextToken();
|
||||
wxString lengthTxt = t.GetNextToken();
|
||||
|
||||
if (!name.IsEmpty() && !startTxt.IsEmpty() && !lengthTxt.IsEmpty()) {
|
||||
unsigned long start;
|
||||
startTxt.ToULong(&start);
|
||||
|
||||
unsigned long length;
|
||||
lengthTxt.ToULong(&length);
|
||||
|
||||
if (start >= m_ambeLength || (start + length) >= m_ambeLength)
|
||||
wxLogError(wxT("The start or end for *%s* is out of range, start: %lu, end: %lu"), name.c_str(), start, start + length);
|
||||
else
|
||||
m_index[name] = new CIndexRecord(name, start + SILENCE_LENGTH, length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
file.Close();
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (C) 2011,2012 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef BeaconUnit_H
|
||||
#define BeaconUnit_H
|
||||
|
||||
#include "SlowDataEncoder.h"
|
||||
#include "BeaconCallback.h"
|
||||
#include "DStarDefines.h"
|
||||
|
||||
#include <wx/wx.h>
|
||||
|
||||
class CIndexRecord {
|
||||
public:
|
||||
CIndexRecord(const wxString& name, unsigned int start, unsigned int length) :
|
||||
m_name(name),
|
||||
m_start(start),
|
||||
m_length(length)
|
||||
{
|
||||
}
|
||||
|
||||
wxString getName() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
unsigned int getStart() const
|
||||
{
|
||||
return m_start;
|
||||
}
|
||||
|
||||
unsigned int getLength() const
|
||||
{
|
||||
return m_length;
|
||||
}
|
||||
|
||||
private:
|
||||
wxString m_name;
|
||||
unsigned int m_start;
|
||||
unsigned int m_length;
|
||||
};
|
||||
|
||||
WX_DECLARE_STRING_HASH_MAP(CIndexRecord*, CIndexList_t);
|
||||
|
||||
class CBeaconUnit {
|
||||
public:
|
||||
CBeaconUnit(IBeaconCallback* handler, const wxString& callsign, const wxString& text, bool voice, TEXT_LANG language);
|
||||
~CBeaconUnit();
|
||||
|
||||
void sendBeacon();
|
||||
|
||||
void clock();
|
||||
|
||||
private:
|
||||
unsigned char* m_ambe;
|
||||
unsigned int m_ambeLength;
|
||||
unsigned char* m_data;
|
||||
unsigned int m_dataLength;
|
||||
CIndexList_t m_index;
|
||||
TEXT_LANG m_language;
|
||||
IBeaconCallback* m_handler;
|
||||
wxString m_callsign;
|
||||
CSlowDataEncoder m_encoder;
|
||||
unsigned int m_in;
|
||||
unsigned int m_out;
|
||||
unsigned int m_seqNo;
|
||||
wxStopWatch m_time;
|
||||
bool m_sending;
|
||||
|
||||
bool lookup(const wxString& id);
|
||||
void spellCallsign(const wxString& callsign);
|
||||
|
||||
bool readAMBE(const wxString& name);
|
||||
bool readIndex(const wxString& name);
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (C) 2009 by Jonathan Naylor, G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "CCITTChecksum.h"
|
||||
|
||||
#include "Utils.h"
|
||||
|
||||
static const unsigned short ccittTab[] = {
|
||||
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
|
||||
0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
|
||||
0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
|
||||
0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
|
||||
0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
|
||||
0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
|
||||
0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
|
||||
0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
|
||||
0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
|
||||
0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
|
||||
0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
|
||||
0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
|
||||
0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
|
||||
0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
|
||||
0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
|
||||
0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
|
||||
0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
|
||||
0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
|
||||
0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
|
||||
0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
|
||||
0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
|
||||
0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
|
||||
0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
|
||||
0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
|
||||
0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
|
||||
0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
|
||||
0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
|
||||
0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
|
||||
0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
|
||||
0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
|
||||
0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
|
||||
0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0};
|
||||
|
||||
CCCITTChecksum::CCCITTChecksum() :
|
||||
m_crc16(0x0000U)
|
||||
{
|
||||
}
|
||||
|
||||
CCCITTChecksum::~CCCITTChecksum()
|
||||
{
|
||||
}
|
||||
|
||||
void CCCITTChecksum::update(const unsigned char* data, unsigned int length)
|
||||
{
|
||||
wxASSERT(data != NULL);
|
||||
|
||||
for (unsigned int i = 0U; i < length; i++)
|
||||
m_crc16 = (wxUint16(m_crc8[0U]) << 8) ^ ccittTab[m_crc8[1U] ^ data[i]];
|
||||
}
|
||||
|
||||
void CCCITTChecksum::result(unsigned char* data)
|
||||
{
|
||||
wxASSERT(data != NULL);
|
||||
|
||||
data[0U] = m_crc8[1U];
|
||||
data[1U] = m_crc8[0U];
|
||||
}
|
||||
|
||||
bool CCCITTChecksum::check(const unsigned char* data)
|
||||
{
|
||||
wxASSERT(data != NULL);
|
||||
|
||||
unsigned char sum[2U];
|
||||
result(sum);
|
||||
|
||||
return sum[0U] == data[0U] && sum[1U] == data[1U];
|
||||
}
|
||||
|
||||
void CCCITTChecksum::reset()
|
||||
{
|
||||
m_crc16 = 0x0000U;
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (C) 2009,2011 by Jonathan Naylor, G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef CCITTChecksum_H
|
||||
#define CCITTChecksum_H
|
||||
|
||||
#include <wx/wx.h>
|
||||
|
||||
class CCCITTChecksum {
|
||||
public:
|
||||
CCCITTChecksum();
|
||||
~CCCITTChecksum();
|
||||
|
||||
void update(const unsigned char* data, unsigned int length);
|
||||
|
||||
void result(unsigned char* data);
|
||||
|
||||
bool check(const unsigned char* data);
|
||||
|
||||
void reset();
|
||||
|
||||
private:
|
||||
union {
|
||||
wxUint16 m_crc16;
|
||||
wxUint8 m_crc8[2U];
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (C) 2009,2011,2014 by Jonathan Naylor, G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "CCITTChecksumReverse.h"
|
||||
|
||||
#include "Utils.h"
|
||||
|
||||
static const unsigned short ccittTab[] = {
|
||||
0x0000,0x1189,0x2312,0x329b,0x4624,0x57ad,0x6536,0x74bf,
|
||||
0x8c48,0x9dc1,0xaf5a,0xbed3,0xca6c,0xdbe5,0xe97e,0xf8f7,
|
||||
0x1081,0x0108,0x3393,0x221a,0x56a5,0x472c,0x75b7,0x643e,
|
||||
0x9cc9,0x8d40,0xbfdb,0xae52,0xdaed,0xcb64,0xf9ff,0xe876,
|
||||
0x2102,0x308b,0x0210,0x1399,0x6726,0x76af,0x4434,0x55bd,
|
||||
0xad4a,0xbcc3,0x8e58,0x9fd1,0xeb6e,0xfae7,0xc87c,0xd9f5,
|
||||
0x3183,0x200a,0x1291,0x0318,0x77a7,0x662e,0x54b5,0x453c,
|
||||
0xbdcb,0xac42,0x9ed9,0x8f50,0xfbef,0xea66,0xd8fd,0xc974,
|
||||
0x4204,0x538d,0x6116,0x709f,0x0420,0x15a9,0x2732,0x36bb,
|
||||
0xce4c,0xdfc5,0xed5e,0xfcd7,0x8868,0x99e1,0xab7a,0xbaf3,
|
||||
0x5285,0x430c,0x7197,0x601e,0x14a1,0x0528,0x37b3,0x263a,
|
||||
0xdecd,0xcf44,0xfddf,0xec56,0x98e9,0x8960,0xbbfb,0xaa72,
|
||||
0x6306,0x728f,0x4014,0x519d,0x2522,0x34ab,0x0630,0x17b9,
|
||||
0xef4e,0xfec7,0xcc5c,0xddd5,0xa96a,0xb8e3,0x8a78,0x9bf1,
|
||||
0x7387,0x620e,0x5095,0x411c,0x35a3,0x242a,0x16b1,0x0738,
|
||||
0xffcf,0xee46,0xdcdd,0xcd54,0xb9eb,0xa862,0x9af9,0x8b70,
|
||||
0x8408,0x9581,0xa71a,0xb693,0xc22c,0xd3a5,0xe13e,0xf0b7,
|
||||
0x0840,0x19c9,0x2b52,0x3adb,0x4e64,0x5fed,0x6d76,0x7cff,
|
||||
0x9489,0x8500,0xb79b,0xa612,0xd2ad,0xc324,0xf1bf,0xe036,
|
||||
0x18c1,0x0948,0x3bd3,0x2a5a,0x5ee5,0x4f6c,0x7df7,0x6c7e,
|
||||
0xa50a,0xb483,0x8618,0x9791,0xe32e,0xf2a7,0xc03c,0xd1b5,
|
||||
0x2942,0x38cb,0x0a50,0x1bd9,0x6f66,0x7eef,0x4c74,0x5dfd,
|
||||
0xb58b,0xa402,0x9699,0x8710,0xf3af,0xe226,0xd0bd,0xc134,
|
||||
0x39c3,0x284a,0x1ad1,0x0b58,0x7fe7,0x6e6e,0x5cf5,0x4d7c,
|
||||
0xc60c,0xd785,0xe51e,0xf497,0x8028,0x91a1,0xa33a,0xb2b3,
|
||||
0x4a44,0x5bcd,0x6956,0x78df,0x0c60,0x1de9,0x2f72,0x3efb,
|
||||
0xd68d,0xc704,0xf59f,0xe416,0x90a9,0x8120,0xb3bb,0xa232,
|
||||
0x5ac5,0x4b4c,0x79d7,0x685e,0x1ce1,0x0d68,0x3ff3,0x2e7a,
|
||||
0xe70e,0xf687,0xc41c,0xd595,0xa12a,0xb0a3,0x8238,0x93b1,
|
||||
0x6b46,0x7acf,0x4854,0x59dd,0x2d62,0x3ceb,0x0e70,0x1ff9,
|
||||
0xf78f,0xe606,0xd49d,0xc514,0xb1ab,0xa022,0x92b9,0x8330,
|
||||
0x7bc7,0x6a4e,0x58d5,0x495c,0x3de3,0x2c6a,0x1ef1,0x0f78};
|
||||
|
||||
CCCITTChecksumReverse::CCCITTChecksumReverse() :
|
||||
m_crc16(0xFFFFU)
|
||||
{
|
||||
}
|
||||
|
||||
CCCITTChecksumReverse::~CCCITTChecksumReverse()
|
||||
{
|
||||
}
|
||||
|
||||
void CCCITTChecksumReverse::update(const unsigned char* data, unsigned int length)
|
||||
{
|
||||
wxASSERT(data != NULL);
|
||||
|
||||
for (unsigned int i = 0U; i < length; i++)
|
||||
m_crc16 = wxUint16(m_crc8[1U]) ^ ccittTab[m_crc8[0U] ^ data[i]];
|
||||
}
|
||||
|
||||
void CCCITTChecksumReverse::result(unsigned char* data)
|
||||
{
|
||||
wxASSERT(data != NULL);
|
||||
|
||||
m_crc16 = ~m_crc16;
|
||||
|
||||
data[0U] = m_crc8[0U];
|
||||
data[1U] = m_crc8[1U];
|
||||
}
|
||||
|
||||
bool CCCITTChecksumReverse::check(const unsigned char* data)
|
||||
{
|
||||
wxASSERT(data != NULL);
|
||||
|
||||
unsigned char sum[2U];
|
||||
result(sum);
|
||||
|
||||
return sum[0U] == data[0U] && sum[1U] == data[1U];
|
||||
}
|
||||
|
||||
void CCCITTChecksumReverse::reset()
|
||||
{
|
||||
m_crc16 = 0xFFFFU;
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (C) 2009,2011,2014 by Jonathan Naylor, G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef CCITTChecksumReverse_H
|
||||
#define CCITTChecksumReverse_H
|
||||
|
||||
#include <wx/wx.h>
|
||||
|
||||
class CCCITTChecksumReverse {
|
||||
public:
|
||||
CCCITTChecksumReverse();
|
||||
~CCCITTChecksumReverse();
|
||||
|
||||
void update(const unsigned char* data, unsigned int length);
|
||||
|
||||
void result(unsigned char* data);
|
||||
|
||||
bool check(const unsigned char* data);
|
||||
|
||||
void reset();
|
||||
|
||||
private:
|
||||
union {
|
||||
wxUint16 m_crc16;
|
||||
wxUint8 m_crc8[2U];
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (C) 2011 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "CallsignList.h"
|
||||
#include "DStarDefines.h"
|
||||
|
||||
#include <wx/textfile.h>
|
||||
|
||||
CCallsignList::CCallsignList(const wxString& filename) :
|
||||
m_filename(filename),
|
||||
m_callsigns()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
CCallsignList::~CCallsignList()
|
||||
{
|
||||
}
|
||||
|
||||
bool CCallsignList::load()
|
||||
{
|
||||
wxTextFile file;
|
||||
|
||||
bool res = file.Open(m_filename);
|
||||
if (!res)
|
||||
return false;
|
||||
|
||||
unsigned int lines = file.GetLineCount();
|
||||
if (lines == 0U) {
|
||||
file.Close();
|
||||
return true;
|
||||
}
|
||||
|
||||
m_callsigns.Alloc(lines);
|
||||
|
||||
wxString callsign = file.GetFirstLine();
|
||||
|
||||
while (!file.Eof()) {
|
||||
callsign.MakeUpper();
|
||||
callsign.Append(wxT(" "));
|
||||
callsign.Truncate(LONG_CALLSIGN_LENGTH);
|
||||
|
||||
m_callsigns.Add(callsign);
|
||||
|
||||
callsign = file.GetNextLine();
|
||||
}
|
||||
|
||||
file.Close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned int CCallsignList::getCount() const
|
||||
{
|
||||
return m_callsigns.GetCount();
|
||||
}
|
||||
|
||||
bool CCallsignList::isInList(const wxString& callsign) const
|
||||
{
|
||||
return m_callsigns.Index(callsign) != wxNOT_FOUND;
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (C) 2011,2013 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef CallsignList_H
|
||||
#define CallsignList_H
|
||||
|
||||
#include <wx/wx.h>
|
||||
|
||||
class CCallsignList {
|
||||
public:
|
||||
CCallsignList(const wxString& filename);
|
||||
~CCallsignList();
|
||||
|
||||
bool load();
|
||||
|
||||
unsigned int getCount() const;
|
||||
|
||||
bool isInList(const wxString& callsign) const;
|
||||
|
||||
private:
|
||||
wxString m_filename;
|
||||
wxArrayString m_callsigns;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,326 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="AMBEFEC.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="AnnouncementUnit.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ArduinoController.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="BeaconUnit.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="CallsignList.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="CCITTChecksum.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="CCITTChecksumReverse.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DStarGMSKDemodulator.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DStarGMSKModulator.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DStarRepeaterConfig.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DummyController.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DVAPController.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DVMegaController.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DVRPTRV1Controller.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DVRPTRV2Controller.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DVRPTRV3Controller.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DVTOOLFileReader.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DVTOOLFileWriter.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ExternalController.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="FIRFilter.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GatewayProtocolHandler.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GMSKController.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GMSKModem.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GMSKModemLibUsb.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GMSKModemWinUSB.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Golay.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="HardwareController.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="HeaderData.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="K8055Controller.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="LogEvent.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Logger.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="MMDVMController.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Modem.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="OutputQueue.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="RepeaterProtocolHandler.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SerialDataController.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SerialLineController.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SerialPortSelector.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SlowDataDecoder.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SlowDataEncoder.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SoundCardController.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SoundCardReaderWriter.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SplitController.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TCPReaderWriter.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Timer.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="UDPReaderWriter.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Utils.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="URIUSBController.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="AMBEFEC.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="AnnouncementCallback.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="AnnouncementUnit.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ArduinoController.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="AudioCallback.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="BeaconCallback.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="BeaconUnit.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CallsignList.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CCITTChecksum.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CCITTChecksumReverse.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DStarDefines.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DStarGMSKDemodulator.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DStarGMSKModulator.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DStarRepeaterConfig.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DummyController.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DVAPController.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DVMegaController.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DVRPTRV1Controller.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DVRPTRV2Controller.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DVRPTRV3Controller.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DVTOOLFileReader.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DVTOOLFileWriter.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ExternalController.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="FIRFilter.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GatewayProtocolHandler.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GMSKController.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GMSKModem.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GMSKModemLibUsb.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GMSKModemWinUSB.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Golay.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="HardwareController.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="HeaderData.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="K8055Controller.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="LogEvent.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Logger.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="lusb0_usb.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="MMDVMController.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Modem.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="OutputQueue.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="RepeaterProtocolHandler.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="RingBuffer.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SerialDataController.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SerialLineController.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SerialPortSelector.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SlowDataDecoder.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SlowDataEncoder.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SoundCardController.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SoundCardReaderWriter.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SplitController.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TCPReaderWriter.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Timer.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="UDPReaderWriter.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Utils.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Version.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="URIUSBController.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@ -0,0 +1,198 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2015 by Jonathan Naylor, G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef DStarDefines_H
|
||||
#define DStarDefines_H
|
||||
|
||||
#include <wx/wx.h>
|
||||
|
||||
const unsigned int DSTAR_GMSK_SYMBOL_RATE = 4800U;
|
||||
const float DSTAR_GMSK_BT = 0.5F;
|
||||
|
||||
const unsigned char DATA_SYNC_BYTES[] = {0x55, 0x2D, 0x16};
|
||||
|
||||
const unsigned char END_PATTERN_BYTES[] = {0x55, 0x55, 0x55, 0x55, 0xC8, 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
const unsigned int END_PATTERN_LENGTH_BYTES = 6U;
|
||||
|
||||
const unsigned char NULL_AMBE_DATA_BYTES[] = {0x9E, 0x8D, 0x32, 0x88, 0x26, 0x1A, 0x3F, 0x61, 0xE8};
|
||||
|
||||
// Note that these are already scrambled, 0x66 0x66 0x66 otherwise
|
||||
const unsigned char NULL_SLOW_DATA_BYTES[] = {0x16, 0x29, 0xF5};
|
||||
|
||||
const unsigned char NULL_FRAME_DATA_BYTES[] = {0x9E, 0x8D, 0x32, 0x88, 0x26, 0x1A, 0x3F, 0x61, 0xE8, 0x16, 0x29, 0xF5};
|
||||
|
||||
const unsigned int VOICE_FRAME_LENGTH_BYTES = 9U;
|
||||
|
||||
const unsigned int DATA_FRAME_LENGTH_BYTES = 3U;
|
||||
|
||||
const unsigned int DV_FRAME_LENGTH_BYTES = VOICE_FRAME_LENGTH_BYTES + DATA_FRAME_LENGTH_BYTES;
|
||||
|
||||
// The length of the end frame, three bytes extra
|
||||
const unsigned int DV_FRAME_MAX_LENGTH_BYTES = DV_FRAME_LENGTH_BYTES + 3U;
|
||||
|
||||
const unsigned int FEC_SECTION_LENGTH_BYTES = 83U;
|
||||
|
||||
const unsigned int RADIO_HEADER_LENGTH_BYTES = 41U;
|
||||
|
||||
const unsigned int DATA_BLOCK_SIZE_BYTES = 21U * DV_FRAME_LENGTH_BYTES;
|
||||
|
||||
const unsigned int DSTAR_RADIO_SAMPLE_RATE = 48000U;
|
||||
const unsigned int DSTAR_RADIO_BLOCK_SIZE = 960U;
|
||||
const unsigned int DSTAR_AUDIO_BLOCK_SIZE = 160U;
|
||||
|
||||
const unsigned int LOCATOR_LENGTH = 6U;
|
||||
const unsigned int LONG_CALLSIGN_LENGTH = 8U;
|
||||
const unsigned int SHORT_CALLSIGN_LENGTH = 4U;
|
||||
|
||||
const unsigned char SLOW_DATA_TYPE_MASK = 0xF0U;
|
||||
const unsigned char SLOW_DATA_TYPE_GPSDATA = 0x30U;
|
||||
const unsigned char SLOW_DATA_TYPE_TEXT = 0x40U;
|
||||
const unsigned char SLOW_DATA_TYPE_HEADER = 0x50U;
|
||||
const unsigned char SLOW_DATA_TYPE_SQUELCH = 0xC0U;
|
||||
const unsigned char SLOW_DATA_LENGTH_MASK = 0x0FU;
|
||||
|
||||
const unsigned int SLOW_DATA_TEXT_LENGTH = 20U;
|
||||
|
||||
const unsigned char SCRAMBLER_BYTE1 = 0x70U;
|
||||
const unsigned char SCRAMBLER_BYTE2 = 0x4FU;
|
||||
const unsigned char SCRAMBLER_BYTE3 = 0x93U;
|
||||
|
||||
const unsigned char DATA_MASK = 0x80U;
|
||||
const unsigned char REPEATER_MASK = 0x40U;
|
||||
const unsigned char INTERRUPTED_MASK = 0x20U;
|
||||
const unsigned char CONTROL_SIGNAL_MASK = 0x10U;
|
||||
const unsigned char URGENT_MASK = 0x08U;
|
||||
|
||||
const unsigned char REPEATER_CONTROL_MASK = 0x07U;
|
||||
const unsigned char REPEATER_CONTROL = 0x07U;
|
||||
const unsigned char AUTO_REPLY = 0x06U;
|
||||
const unsigned char RESEND_REQUESTED = 0x04U;
|
||||
const unsigned char ACK_FLAG = 0x03U;
|
||||
const unsigned char NO_RESPONSE = 0x02U;
|
||||
const unsigned char RELAY_UNAVAILABLE = 0x01U;
|
||||
|
||||
const unsigned int DSTAR_RADIO_BIT_LENGTH = DSTAR_RADIO_SAMPLE_RATE / DSTAR_GMSK_SYMBOL_RATE;
|
||||
|
||||
const unsigned int DSTAR_FRAME_TIME_MS = 20U;
|
||||
const unsigned int DSTAR_FRAMES_PER_SEC = 50U;
|
||||
|
||||
const unsigned int DSTAR_TICKS_PER_SEC = DSTAR_RADIO_SAMPLE_RATE / DSTAR_RADIO_BLOCK_SIZE;
|
||||
|
||||
const unsigned int DEXTRA_PORT = 30001U;
|
||||
const unsigned int DCS_PORT = 30051U;
|
||||
|
||||
const unsigned int GMSK_MODEM_DATA_LENGTH = 8U;
|
||||
|
||||
const unsigned int RADIO_RUN_FRAME_COUNT = 5U;
|
||||
const unsigned int LOCAL_RUN_FRAME_COUNT = 1U;
|
||||
const unsigned int NETWORK_RUN_FRAME_COUNT = 25U;
|
||||
|
||||
const unsigned int DSTAR_BLEEP_FREQ = 2000U;
|
||||
const unsigned int DSTAR_BLEEP_LENGTH = 100U;
|
||||
const float DSTAR_BLEEP_AMPL = 0.5F;
|
||||
|
||||
const unsigned int NETWORK_TIMEOUT = 2U;
|
||||
|
||||
const unsigned int SPLIT_RX_GUI_COUNT = 5U;
|
||||
const unsigned int SPLIT_RX_COUNT = 25U;
|
||||
const unsigned int SPLIT_TX_GUI_COUNT = 3U;
|
||||
const unsigned int SPLIT_TX_COUNT = 5U;
|
||||
|
||||
enum DSTAR_RPT_STATE {
|
||||
DSRS_SHUTDOWN,
|
||||
DSRS_LISTENING,
|
||||
DSRS_VALID,
|
||||
DSRS_VALID_WAIT,
|
||||
DSRS_INVALID,
|
||||
DSRS_INVALID_WAIT,
|
||||
DSRS_TIMEOUT,
|
||||
DSRS_TIMEOUT_WAIT,
|
||||
DSRS_NETWORK
|
||||
};
|
||||
|
||||
enum NETWORK_TYPE {
|
||||
NETWORK_NONE,
|
||||
NETWORK_HEADER,
|
||||
NETWORK_DATA,
|
||||
NETWORK_TEXT,
|
||||
NETWORK_TEMPTEXT,
|
||||
NETWORK_STATUS1,
|
||||
NETWORK_STATUS2,
|
||||
NETWORK_STATUS3,
|
||||
NETWORK_STATUS4,
|
||||
NETWORK_STATUS5,
|
||||
NETWORK_REGISTER
|
||||
};
|
||||
|
||||
enum DSTAR_MODE {
|
||||
MODE_DUPLEX,
|
||||
MODE_SIMPLEX,
|
||||
MODE_GATEWAY,
|
||||
MODE_TXONLY,
|
||||
MODE_RXONLY,
|
||||
MODE_TXANDRX
|
||||
};
|
||||
|
||||
enum LINK_STATUS {
|
||||
LS_NONE,
|
||||
LS_PENDING_IRCDDB,
|
||||
LS_LINKING_LOOPBACK,
|
||||
LS_LINKING_DEXTRA,
|
||||
LS_LINKING_DPLUS,
|
||||
LS_LINKING_DCS,
|
||||
LS_LINKING_CCS,
|
||||
LS_LINKED_LOOPBACK,
|
||||
LS_LINKED_DEXTRA,
|
||||
LS_LINKED_DPLUS,
|
||||
LS_LINKED_DCS,
|
||||
LS_LINKED_CCS
|
||||
};
|
||||
|
||||
enum ACK_TYPE {
|
||||
AT_NONE,
|
||||
AT_BER,
|
||||
AT_STATUS
|
||||
};
|
||||
|
||||
enum TEXT_LANG {
|
||||
TL_ENGLISH_UK,
|
||||
TL_DEUTSCH,
|
||||
TL_DANSK,
|
||||
TL_FRANCAIS,
|
||||
TL_ITALIANO,
|
||||
TL_POLSKI,
|
||||
TL_ESPANOL,
|
||||
TL_SVENSKA,
|
||||
TL_NEDERLANDS,
|
||||
TL_ENGLISH_US,
|
||||
TL_NORSK
|
||||
};
|
||||
|
||||
enum CONNECTION_TYPE {
|
||||
CT_USB,
|
||||
CT_NETWORK
|
||||
};
|
||||
|
||||
enum USB_INTERFACE {
|
||||
UI_LIBUSB,
|
||||
UI_WINUSB
|
||||
};
|
||||
|
||||
enum DVMEGA_VARIANT {
|
||||
DVMV_MODEM,
|
||||
DVMV_RADIO_2M,
|
||||
DVMV_RADIO_70CM,
|
||||
DVMV_RADIO_2M_70CM
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright (C) 2009,2010,2013,2014,2015 by Jonathan Naylor, G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "DStarGMSKDemodulator.h"
|
||||
|
||||
// Generated by gaussfir(0.5, 4, 10)
|
||||
const wxFloat32 COEFFS_TABLE[] = {
|
||||
0.000000000000003F, 0.000000000000065F, 0.000000000001037F, 0.000000000014448F, 0.000000000174579F,
|
||||
0.000000001829471F, 0.000000016627294F, 0.000000131062698F, 0.000000895979719F, 0.000005312253663F, 0.000027316243802F,
|
||||
0.000121821714020F, 0.000471183399421F, 0.001580581180127F, 0.004598383433830F, 0.011602594308899F, 0.025390226926262F,
|
||||
0.048188078330624F, 0.079318443411643F, 0.113232294527059F, 0.140193533802410F, 0.150538369557851F, 0.140193533802410F,
|
||||
0.113232294527059F, 0.079318443411643F, 0.048188078330623F, 0.025390226926262F, 0.011602594308899F, 0.004598383433830F,
|
||||
0.001580581180127F, 0.000471183399421F, 0.000121821714020F, 0.000027316243802F, 0.000005312253663F, 0.000000895979719F,
|
||||
0.000000131062698F, 0.000000016627294F, 0.000000001829471F, 0.000000000174579F, 0.000000000014448F, 0.000000000001037F,
|
||||
0.000000000000065F, 0.000000000000003F};
|
||||
|
||||
const unsigned int COEFFS_LENGTH = 43U;
|
||||
|
||||
const unsigned int DCOFFSET_COUNT = 4800U; // 5 seconds
|
||||
|
||||
const unsigned int PLLMAX = 0x10000U;
|
||||
const unsigned int PLLINC = PLLMAX / DSTAR_RADIO_BIT_LENGTH;
|
||||
const unsigned int INC_LOCK = PLLINC / 64U;
|
||||
const unsigned int INC_UNLOCK = PLLINC / 32U;
|
||||
|
||||
CDStarGMSKDemodulator::CDStarGMSKDemodulator() :
|
||||
m_filter(COEFFS_TABLE, COEFFS_LENGTH),
|
||||
m_invert(false),
|
||||
m_pll(0U),
|
||||
m_prev(false),
|
||||
m_inc(INC_UNLOCK),
|
||||
m_locked(false),
|
||||
m_offset(0.0F),
|
||||
m_accum(0.0F),
|
||||
m_count(0U)
|
||||
{
|
||||
}
|
||||
|
||||
CDStarGMSKDemodulator::~CDStarGMSKDemodulator()
|
||||
{
|
||||
}
|
||||
|
||||
TRISTATE CDStarGMSKDemodulator::decode(wxFloat32 val)
|
||||
{
|
||||
TRISTATE state = STATE_UNKNOWN;
|
||||
|
||||
// Calculate the DC offset when we are locked
|
||||
if (m_locked) {
|
||||
m_accum += val;
|
||||
m_count++;
|
||||
|
||||
if (m_count >= DCOFFSET_COUNT) {
|
||||
m_accum /= wxFloat32(DCOFFSET_COUNT);
|
||||
|
||||
m_offset = m_offset * 0.9F + m_accum * 0.1F;
|
||||
|
||||
m_accum = 0.0F;
|
||||
m_count = 0U;
|
||||
}
|
||||
}
|
||||
|
||||
wxFloat32 out = m_filter.process(val - m_offset);
|
||||
|
||||
bool bit = out > 0.0F;
|
||||
|
||||
if (bit != m_prev) {
|
||||
if (m_pll < (PLLMAX / 2U))
|
||||
m_pll += m_inc;
|
||||
else
|
||||
m_pll -= m_inc;
|
||||
}
|
||||
|
||||
m_prev = bit;
|
||||
|
||||
m_pll += PLLINC;
|
||||
|
||||
if (m_pll >= PLLMAX) {
|
||||
if (m_invert)
|
||||
state = bit ? STATE_TRUE : STATE_FALSE;
|
||||
else
|
||||
state = bit ? STATE_FALSE : STATE_TRUE;
|
||||
|
||||
m_pll -= PLLMAX;
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
void CDStarGMSKDemodulator::reset()
|
||||
{
|
||||
m_pll = 0U;
|
||||
m_prev = false;
|
||||
m_inc = INC_UNLOCK;
|
||||
m_locked = false;
|
||||
}
|
||||
|
||||
void CDStarGMSKDemodulator::setInvert(bool set)
|
||||
{
|
||||
m_invert = set;
|
||||
}
|
||||
|
||||
void CDStarGMSKDemodulator::lock(bool on)
|
||||
{
|
||||
// Debugging only XXX
|
||||
wxLogMessage(wxT("Current DC offset: %f"), m_offset);
|
||||
|
||||
m_locked = on;
|
||||
|
||||
m_inc = on ? INC_LOCK : INC_UNLOCK;
|
||||
}
|
||||
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2009,2010,2013,2014 by Jonathan Naylor, G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef DStarGMSKDemodulator_H
|
||||
#define DStarGMSKDemodulator_H
|
||||
|
||||
#include "DStarDefines.h"
|
||||
#include "FIRFilter.h"
|
||||
#include "Utils.h"
|
||||
|
||||
class CDStarGMSKDemodulator {
|
||||
public:
|
||||
CDStarGMSKDemodulator();
|
||||
~CDStarGMSKDemodulator();
|
||||
|
||||
TRISTATE decode(wxFloat32 val);
|
||||
|
||||
void setInvert(bool set);
|
||||
|
||||
void reset();
|
||||
|
||||
void lock(bool on);
|
||||
|
||||
private:
|
||||
CFIRFilter m_filter;
|
||||
bool m_invert;
|
||||
unsigned int m_pll;
|
||||
bool m_prev;
|
||||
unsigned int m_inc;
|
||||
bool m_locked;
|
||||
wxFloat32 m_offset;
|
||||
wxFloat32 m_accum;
|
||||
unsigned int m_count;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (C) 2009,2015 by Jonathan Naylor, G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "DStarGMSKModulator.h"
|
||||
|
||||
#include "DStarDefines.h"
|
||||
|
||||
// Generated by gaussfir(0.5, 4, 10)
|
||||
const wxFloat32 COEFFS_TABLE[] = {
|
||||
0.000000000000003F, 0.000000000000065F, 0.000000000001037F, 0.000000000014448F, 0.000000000174579F,
|
||||
0.000000001829471F, 0.000000016627294F, 0.000000131062698F, 0.000000895979719F, 0.000005312253663F, 0.000027316243802F,
|
||||
0.000121821714020F, 0.000471183399421F, 0.001580581180127F, 0.004598383433830F, 0.011602594308899F, 0.025390226926262F,
|
||||
0.048188078330624F, 0.079318443411643F, 0.113232294527059F, 0.140193533802410F, 0.150538369557851F, 0.140193533802410F,
|
||||
0.113232294527059F, 0.079318443411643F, 0.048188078330623F, 0.025390226926262F, 0.011602594308899F, 0.004598383433830F,
|
||||
0.001580581180127F, 0.000471183399421F, 0.000121821714020F, 0.000027316243802F, 0.000005312253663F, 0.000000895979719F,
|
||||
0.000000131062698F, 0.000000016627294F, 0.000000001829471F, 0.000000000174579F, 0.000000000014448F, 0.000000000001037F,
|
||||
0.000000000000065F, 0.000000000000003F};
|
||||
|
||||
const unsigned int COEFFS_LENGTH = 43U;
|
||||
|
||||
CDStarGMSKModulator::CDStarGMSKModulator() :
|
||||
m_filter(COEFFS_TABLE, COEFFS_LENGTH),
|
||||
m_invert(false)
|
||||
{
|
||||
}
|
||||
|
||||
CDStarGMSKModulator::~CDStarGMSKModulator()
|
||||
{
|
||||
}
|
||||
|
||||
unsigned int CDStarGMSKModulator::code(bool bit, wxFloat32* buffer, unsigned int length)
|
||||
{
|
||||
wxASSERT(buffer != NULL);
|
||||
wxASSERT(length == DSTAR_RADIO_BIT_LENGTH);
|
||||
|
||||
if (m_invert)
|
||||
bit = !bit;
|
||||
|
||||
for (unsigned int i = 0U; i < DSTAR_RADIO_BIT_LENGTH; i++) {
|
||||
if (bit)
|
||||
buffer[i] = m_filter.process(-0.5F);
|
||||
else
|
||||
buffer[i] = m_filter.process(0.5F);
|
||||
}
|
||||
|
||||
return DSTAR_RADIO_BIT_LENGTH;
|
||||
}
|
||||
|
||||
void CDStarGMSKModulator::setInvert(bool set)
|
||||
{
|
||||
m_invert = set;
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 2009,2013 by Jonathan Naylor, G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef DStarGMSKModulator_H
|
||||
#define DStarGMSKModulator_H
|
||||
|
||||
#include "FIRFilter.h"
|
||||
|
||||
class CDStarGMSKModulator {
|
||||
public:
|
||||
CDStarGMSKModulator();
|
||||
~CDStarGMSKModulator();
|
||||
|
||||
unsigned int code(bool bit, wxFloat32* buffer, unsigned int length);
|
||||
|
||||
void setInvert(bool set);
|
||||
|
||||
private:
|
||||
CFIRFilter m_filter;
|
||||
bool m_invert;
|
||||
};
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,243 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2015 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef DStarRepeaterConfig_H
|
||||
#define DStarRepeaterConfig_H
|
||||
|
||||
#include "DStarDefines.h"
|
||||
|
||||
#include <wx/wx.h>
|
||||
#include <wx/config.h>
|
||||
#include <wx/filename.h>
|
||||
|
||||
class CDStarRepeaterConfig {
|
||||
public:
|
||||
#if defined(__WINDOWS__)
|
||||
CDStarRepeaterConfig(wxConfigBase* config, const wxString& dir, const wxString& configName, const wxString& name);
|
||||
#else
|
||||
CDStarRepeaterConfig(const wxString& dir, const wxString& configName, const wxString& name, const bool mustExist=false);
|
||||
#endif
|
||||
~CDStarRepeaterConfig();
|
||||
|
||||
void getCallsign(wxString& callsign, wxString& gateway, DSTAR_MODE& mode, ACK_TYPE& ack, bool& restriction, bool& rpt1Validation, bool& dtmfBlanking, bool& errorReply) const;
|
||||
void setCallsign(const wxString& callsign, const wxString& gateway, DSTAR_MODE mode, ACK_TYPE ack, bool restriction, bool rpt1Validation, bool dtmfBlanking, bool errorReply);
|
||||
|
||||
void getNetwork(wxString& gatewayAddress, unsigned int& gatewayPort, wxString& localAddress, unsigned int& localPort, wxString& name) const;
|
||||
void setNetwork(const wxString& gatewayAddress, unsigned int gatewayPort, const wxString& localAddress, unsigned int localPort, const wxString& name);
|
||||
|
||||
void getModem(wxString& type) const;
|
||||
void setModem(const wxString& type);
|
||||
|
||||
void getTimes(unsigned int& timeout, unsigned int& ackTime) const;
|
||||
void setTimes(unsigned int timeout, unsigned int ackTime);
|
||||
|
||||
void getBeacon(unsigned int& time, wxString& text, bool& voice, TEXT_LANG& language) const;
|
||||
void setBeacon(unsigned int time, const wxString& text, bool voice, TEXT_LANG language);
|
||||
|
||||
void getAnnouncement(bool& enabled, unsigned int& time, wxString& recordRPT1, wxString& recordRPT2, wxString& deleteRPT1, wxString& deleteRPT2) const;
|
||||
void setAnnouncement(bool enabled, unsigned int time, const wxString& recordRPT1, const wxString& recordRPT2, const wxString& deleteRPT1, const wxString& deleteRPT2);
|
||||
|
||||
void getControl(bool& enabled, wxString& rpt1Callsign, wxString& rpt2Callsign, wxString& shutdown, wxString& startup, wxString& status1, wxString& status2, wxString& status3, wxString& status4, wxString& status5, wxString& command1, wxString& command1Line, wxString& command2, wxString& command2Line, wxString& command5, wxString& command5Line, wxString& command6, wxString& command6Line, wxString& command3, wxString& command3Line, wxString& command4, wxString& command4Line, wxString& output1, wxString& output2, wxString& output3, wxString& output4) const;
|
||||
|
||||
void setControl(bool enabled, const wxString& rpt1Callsign, const wxString& rpt2Callsign, const wxString& shutdown, const wxString& startup, const wxString& status1, const wxString& status2, const wxString& status3, const wxString& status4, const wxString& status5, const wxString& command1, const wxString& command1Line, const wxString& command2, const wxString& command2Line, const wxString& command3, const wxString& command3Line, const wxString& command4, const wxString& command4Line, const wxString& command5, const wxString& command5Line, const wxString& command6, const wxString& command6Line, const wxString& output1, const wxString& output2, const wxString& output3, const wxString& output4);
|
||||
|
||||
void getController(wxString& type, unsigned int& serialConfig, bool& pttInvert, unsigned int& activeHangTime) const;
|
||||
void setController(const wxString& type, unsigned int serialConfig, bool pttInvert, unsigned int activeHangTime);
|
||||
|
||||
void getOutputs(bool& out1, bool& out2, bool& out3, bool& out4) const;
|
||||
void setOutputs(bool out1, bool out2, bool out3, bool out4);
|
||||
|
||||
void getLogging(bool& logging) const;
|
||||
void setLogging(bool logging);
|
||||
|
||||
void getPosition(int& x, int& y) const;
|
||||
void setPosition(int x, int y);
|
||||
|
||||
void getDVAP(wxString& port, unsigned int& frequency, int& power, int& squelch) const;
|
||||
void setDVAP(const wxString& port, unsigned int frequency, int power, int squelch);
|
||||
|
||||
void getGMSK(USB_INTERFACE& type, unsigned int& address) const;
|
||||
void setGMSK(USB_INTERFACE type, unsigned int address);
|
||||
|
||||
void getDVRPTR1(wxString& port, bool& rxInvert, bool& txInvert, bool& channel, unsigned int& modLevel, unsigned int& txDelay) const;
|
||||
void setDVRPTR1(const wxString& port, bool rxInvert, bool txInvert, bool channel, unsigned int modLevel, unsigned int txDelay);
|
||||
|
||||
void getDVRPTR2(CONNECTION_TYPE& connectionType, wxString& usbPort, wxString& address, unsigned int& port, bool& txInvert, unsigned int& modLevel, unsigned int& txDelay) const;
|
||||
void setDVRPTR2(CONNECTION_TYPE connectionType, const wxString& usbPort, const wxString& address, unsigned int port, bool txInvert, unsigned int modLevel, unsigned int txDelay);
|
||||
|
||||
void getDVRPTR3(CONNECTION_TYPE& connectionType, wxString& usbPort, wxString& address, unsigned int& port, bool& txInvert, unsigned int& modLevel, unsigned int& txDelay) const;
|
||||
void setDVRPTR3(CONNECTION_TYPE connectionType, const wxString& usbPort, const wxString& address, unsigned int port, bool txInvert, unsigned int modLevel, unsigned int txDelay);
|
||||
|
||||
void getDVMEGA(wxString& port, DVMEGA_VARIANT& variant, bool& rxInvert, bool& txInvert, unsigned int& txDelay, unsigned int& rxFrequency, unsigned int& txFrequency, unsigned int& power) const;
|
||||
void setDVMEGA(const wxString& port, DVMEGA_VARIANT variant, bool rxInvert, bool txInvert, unsigned int txDelay, unsigned int rxFrequency, unsigned int txFrequency, unsigned int power);
|
||||
|
||||
void getMMDVM(wxString& port, bool& rxInvert, bool& txInvert, bool& pttInvert, unsigned int& txDelay, unsigned int& rxLevel, unsigned int& txLevel) const;
|
||||
void setMMDVM(const wxString& port, bool rxInvert, bool txInvert, bool pttInvert, unsigned int txDelay, unsigned int rxLevel, unsigned int txLevel);
|
||||
|
||||
void getSoundCard(wxString& rxDevice, wxString& txDevice, bool& rxInvert, bool& txInvert, wxFloat32& rxLevel, wxFloat32& txLevel, unsigned int& txDelay, unsigned int& txTail) const;
|
||||
void setSoundCard(const wxString& rxDevice, const wxString& txDevice, bool rxInvert, bool txInvert, wxFloat32 rxLevel, wxFloat32 txLevel, unsigned int txDelay, unsigned int txTail);
|
||||
|
||||
void getSplit(wxString& localAddress, unsigned int& localPort, wxArrayString& transmitterNames, wxArrayString& receiverNames, unsigned int& timeout) const;
|
||||
void setSplit(const wxString& localAddress, unsigned int localPort, const wxArrayString& transmitterNames, const wxArrayString& receiverNames, unsigned int timeout);
|
||||
|
||||
bool write();
|
||||
|
||||
private:
|
||||
#if defined(__WINDOWS__)
|
||||
wxConfigBase* m_config;
|
||||
wxString m_name;
|
||||
#endif
|
||||
wxFileName m_fileName;
|
||||
wxString m_callsign;
|
||||
wxString m_gateway;
|
||||
DSTAR_MODE m_mode;
|
||||
ACK_TYPE m_ack;
|
||||
bool m_restriction;
|
||||
bool m_rpt1Validation;
|
||||
bool m_dtmfBlanking;
|
||||
bool m_errorReply;
|
||||
wxString m_gatewayAddress;
|
||||
unsigned int m_gatewayPort;
|
||||
wxString m_localAddress;
|
||||
unsigned int m_localPort;
|
||||
wxString m_networkName;
|
||||
wxString m_modemType;
|
||||
unsigned int m_timeout;
|
||||
unsigned int m_ackTime;
|
||||
unsigned int m_beaconTime;
|
||||
wxString m_beaconText;
|
||||
bool m_beaconVoice;
|
||||
TEXT_LANG m_language;
|
||||
bool m_announcementEnabled;
|
||||
unsigned int m_announcementTime;
|
||||
wxString m_announcementRecordRPT1;
|
||||
wxString m_announcementRecordRPT2;
|
||||
wxString m_announcementDeleteRPT1;
|
||||
wxString m_announcementDeleteRPT2;
|
||||
bool m_controlEnabled;
|
||||
wxString m_controlRpt1Callsign;
|
||||
wxString m_controlRpt2Callsign;
|
||||
wxString m_controlShutdown;
|
||||
wxString m_controlStartup;
|
||||
wxString m_controlStatus1;
|
||||
wxString m_controlStatus2;
|
||||
wxString m_controlStatus3;
|
||||
wxString m_controlStatus4;
|
||||
wxString m_controlStatus5;
|
||||
wxString m_controlCommand1;
|
||||
wxString m_controlCommand1Line;
|
||||
wxString m_controlCommand2;
|
||||
wxString m_controlCommand2Line;
|
||||
wxString m_controlCommand3;
|
||||
wxString m_controlCommand3Line;
|
||||
wxString m_controlCommand4;
|
||||
wxString m_controlCommand4Line;
|
||||
wxString m_controlCommand5;
|
||||
wxString m_controlCommand5Line;
|
||||
wxString m_controlCommand6;
|
||||
wxString m_controlCommand6Line;
|
||||
wxString m_controlOutput1;
|
||||
wxString m_controlOutput2;
|
||||
wxString m_controlOutput3;
|
||||
wxString m_controlOutput4;
|
||||
wxString m_controllerType;
|
||||
unsigned int m_serialConfig;
|
||||
bool m_pttInvert;
|
||||
unsigned int m_activeHangTime;
|
||||
bool m_output1;
|
||||
bool m_output2;
|
||||
bool m_output3;
|
||||
bool m_output4;
|
||||
bool m_logging;
|
||||
int m_x;
|
||||
int m_y;
|
||||
|
||||
// DVAP
|
||||
wxString m_dvapPort;
|
||||
unsigned int m_dvapFrequency;
|
||||
int m_dvapPower;
|
||||
int m_dvapSquelch;
|
||||
|
||||
// GMSK
|
||||
USB_INTERFACE m_gmskInterface;
|
||||
unsigned int m_gmskAddress;
|
||||
|
||||
// DV-RPTR 1
|
||||
wxString m_dvrptr1Port;
|
||||
bool m_dvrptr1RXInvert;
|
||||
bool m_dvrptr1TXInvert;
|
||||
bool m_dvrptr1Channel;
|
||||
unsigned int m_dvrptr1ModLevel;
|
||||
unsigned int m_dvrptr1TXDelay;
|
||||
|
||||
// DV-RPTR 2
|
||||
CONNECTION_TYPE m_dvrptr2Connection;
|
||||
wxString m_dvrptr2USBPort;
|
||||
wxString m_dvrptr2Address;
|
||||
unsigned int m_dvrptr2Port;
|
||||
bool m_dvrptr2TXInvert;
|
||||
unsigned int m_dvrptr2ModLevel;
|
||||
unsigned int m_dvrptr2TXDelay;
|
||||
|
||||
// DV-RPTR 3
|
||||
CONNECTION_TYPE m_dvrptr3Connection;
|
||||
wxString m_dvrptr3USBPort;
|
||||
wxString m_dvrptr3Address;
|
||||
unsigned int m_dvrptr3Port;
|
||||
bool m_dvrptr3TXInvert;
|
||||
unsigned int m_dvrptr3ModLevel;
|
||||
unsigned int m_dvrptr3TXDelay;
|
||||
|
||||
// DVMEGA
|
||||
wxString m_dvmegaPort;
|
||||
DVMEGA_VARIANT m_dvmegaVariant;
|
||||
bool m_dvmegaRXInvert;
|
||||
bool m_dvmegaTXInvert;
|
||||
unsigned int m_dvmegaTXDelay;
|
||||
unsigned int m_dvmegaRXFrequency;
|
||||
unsigned int m_dvmegaTXFrequency;
|
||||
unsigned int m_dvmegaPower;
|
||||
|
||||
// MMDVM
|
||||
wxString m_mmdvmPort;
|
||||
bool m_mmdvmRXInvert;
|
||||
bool m_mmdvmTXInvert;
|
||||
bool m_mmdvmPTTInvert;
|
||||
unsigned int m_mmdvmTXDelay;
|
||||
unsigned int m_mmdvmRXLevel;
|
||||
unsigned int m_mmdvmTXLevel;
|
||||
|
||||
// Sound Card
|
||||
wxString m_soundCardRXDevice;
|
||||
wxString m_soundCardTXDevice;
|
||||
bool m_soundCardRXInvert;
|
||||
bool m_soundCardTXInvert;
|
||||
wxFloat32 m_soundCardRXLevel;
|
||||
wxFloat32 m_soundCardTXLevel;
|
||||
unsigned int m_soundCardTXDelay;
|
||||
unsigned int m_soundCardTXTail;
|
||||
|
||||
// Split
|
||||
wxString m_splitLocalAddress;
|
||||
unsigned int m_splitLocalPort;
|
||||
wxArrayString m_splitTXNames;
|
||||
wxArrayString m_splitRXNames;
|
||||
unsigned int m_splitTimeout;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Copyright (C) 2009 by Jonathan Naylor, G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "DStarScrambler.h"
|
||||
|
||||
#include <wx/wx.h>
|
||||
|
||||
static const bool SCRAMBLER_TABLE_BITS[] = {
|
||||
false, false, false, false, true, true, true, false, true, true, true, true, false, false, true, false,
|
||||
true, true, false, false, true, false, false, true, false, false, false, false, false, false, true, false,
|
||||
false, false, true, false, false, true, true, false, false, false, true, false, true, true, true, false,
|
||||
true, false, true, true, false, true, true, false, false, false, false, false, true, true, false, false,
|
||||
true, true, false, true, false, true, false, false, true, true, true, false, false, true, true, true,
|
||||
true, false, true, true, false, true, false, false, false, false, true, false, true, false, true, false,
|
||||
true, true, true, true, true, false, true, false, false, true, false, true, false, false, false, true,
|
||||
true, false, true, true, true, false, false, false, true, true, true, true, true, true, true, false,
|
||||
false, false, false, true, true, true, false, true, true, true, true, false, false, true, false, true,
|
||||
true, false, false, true, false, false, true, false, false, false, false, false, false, true, false, false,
|
||||
false, true, false, false, true, true, false, false, false, true, false, true, true, true, false, true,
|
||||
false, true, true, false, true, true, false, false, false, false, false, true, true, false, false, true,
|
||||
true, false, true, false, true, false, false, true, true, true, false, false, true, true, true, true,
|
||||
false, true, true, false, true, false, false, false, false, true, false, true, false, true, false, true,
|
||||
true, true, true, true, false, true, false, false, true, false, true, false, false, false, true, true,
|
||||
false, true, true, true, false, false, false, true, true, true, true, true, true, true, false, false,
|
||||
false, false, true, true, true, false, true, true, true, true, false, false, true, false, true, true,
|
||||
false, false, true, false, false, true, false, false, false, false, false, false, true, false, false, false,
|
||||
true, false, false, true, true, false, false, false, true, false, true, true, true, false, true, false,
|
||||
true, true, false, true, true, false, false, false, false, false, true, true, false, false, true, true,
|
||||
false, true, false, true, false, false, true, true, true, false, false, true, true, true, true, false,
|
||||
true, true, false, true, false, false, false, false, true, false, true, false, true, false, true, true,
|
||||
true, true, true, false, true, false, false, true, false, true, false, false, false, true, true, false,
|
||||
true, true, true, false, false, false, true, true, true, true, true, true, true, false, false, false,
|
||||
false, true, true, true, false, true, true, true, true, false, false, true, false, true, true, false,
|
||||
false, true, false, false, true, false, false, false, false, false, false, true, false, false, false, true,
|
||||
false, false, true, true, false, false, false, true, false, true, true, true, false, true, false, true,
|
||||
true, false, true, true, false, false, false, false, false, true, true, false, false, true, true, false,
|
||||
true, false, true, false, false, true, true, true, false, false, true, true, true, true, false, true,
|
||||
true, false, true, false, false, false, false, true, false, true, false, true, false, true, true, true,
|
||||
true, true, false, true, false, false, true, false, true, false, false, false, true, true, false, true,
|
||||
true, true, false, false, false, true, true, true, true, true, true, true, false, false, false, false,
|
||||
true, true, true, false, true, true, true, true, false, false, true, false, true, true, false, false,
|
||||
true, false, false, true, false, false, false, false, false, false, true, false, false, false, true, false,
|
||||
false, true, true, false, false, false, true, false, true, true, true, false, true, false, true, true,
|
||||
false, true, true, false, false, false, false, false, true, true, false, false, true, true, false, true,
|
||||
false, true, false, false, true, true, true, false, false, true, true, true, true, false, true, true,
|
||||
false, true, false, false, false, false, true, false, true, false, true, false, true, true, true, true,
|
||||
true, false, true, false, false, true, false, true, false, false, false, true, true, false, true, true,
|
||||
true, false, false, false, true, true, true, true, true, true, true, false, false, false, false, true,
|
||||
true, true, false, true, true, true, true, false, false, true, false, true, true, false, false, true,
|
||||
false, false, true, false, false, false, false, false, false, true, false, false, false, true, false, false,
|
||||
true, true, false, false, false, true, false, true, true, true, false, true, false, true, true, false,
|
||||
true, true, false, false, false, false, false, true, true, false, false, true, true, false, true, false,
|
||||
true, false, false, true, true, true, false, false, true, true, true, true, false, true, true, false};
|
||||
|
||||
static const unsigned int SCRAMBLER_TABLE_BITS_LENGTH = 720U;
|
||||
|
||||
static const unsigned char SCRAMBLER_TABLE_BYTES[] = {
|
||||
0x0e, 0xf2, 0xc9, 0x02, 0x26, 0x2e, 0xb6, 0x0c, 0xd4, 0xe7, 0xb4, 0x2a, 0xfa, 0x51, 0xb8, 0xfe,
|
||||
0x1d, 0xe5, 0x92, 0x04, 0x4c, 0x5d, 0x6c, 0x19, 0xa9, 0xcf, 0x68, 0x55, 0xf4, 0xa3, 0x71, 0xfc,
|
||||
0x3b, 0xcb, 0x24, 0x08, 0x98, 0xba, 0xd8, 0x33, 0x53, 0x9e, 0xd0, 0xab, 0xe9, 0x46, 0xe3, 0xf8,
|
||||
0x77, 0x96, 0x48, 0x11, 0x31, 0x75, 0xb0, 0x66, 0xa7, 0x3d, 0xa1, 0x57, 0xd2, 0x8d, 0xc7, 0xf0,
|
||||
0xef, 0x2c, 0x90, 0x22, 0x62, 0xeb, 0x60, 0xcd, 0x4e, 0x7b, 0x42, 0xaf, 0xa5, 0x1b, 0x8f, 0xe1,
|
||||
0xde, 0x59, 0x20, 0x44, 0xc5, 0xd6, 0xc1, 0x9a, 0x9c, 0xf6};
|
||||
|
||||
static const unsigned int SCRAMBLER_TABLE_BYTES_LENGTH = 90U;
|
||||
|
||||
CDStarScrambler::CDStarScrambler() :
|
||||
m_count(0U)
|
||||
{
|
||||
}
|
||||
|
||||
CDStarScrambler::~CDStarScrambler()
|
||||
{
|
||||
}
|
||||
|
||||
void CDStarScrambler::process(bool* inOut, unsigned int length)
|
||||
{
|
||||
wxASSERT(inOut != 0);
|
||||
|
||||
for (unsigned int i = 0U; i < length; i++) {
|
||||
inOut[i] ^= SCRAMBLER_TABLE_BITS[m_count++];
|
||||
|
||||
if (m_count >= SCRAMBLER_TABLE_BITS_LENGTH)
|
||||
m_count = 0U;
|
||||
}
|
||||
}
|
||||
|
||||
void CDStarScrambler::process(const bool* in, bool* out, unsigned int length)
|
||||
{
|
||||
wxASSERT(in != 0);
|
||||
wxASSERT(out != 0);
|
||||
|
||||
for (unsigned int i = 0U; i < length; i++) {
|
||||
out[i] = in[i] ^ SCRAMBLER_TABLE_BITS[m_count++];
|
||||
|
||||
if (m_count >= SCRAMBLER_TABLE_BITS_LENGTH)
|
||||
m_count = 0U;
|
||||
}
|
||||
}
|
||||
|
||||
void CDStarScrambler::process(unsigned char* inOut, unsigned int length)
|
||||
{
|
||||
wxASSERT(inOut != 0);
|
||||
|
||||
for (unsigned int i = 0U; i < length; i++) {
|
||||
inOut[i] ^= SCRAMBLER_TABLE_BYTES[m_count++];
|
||||
|
||||
if (m_count >= SCRAMBLER_TABLE_BYTES_LENGTH)
|
||||
m_count = 0U;
|
||||
}
|
||||
}
|
||||
|
||||
void CDStarScrambler::process(const unsigned char* in, unsigned char* out, unsigned int length)
|
||||
{
|
||||
wxASSERT(in != 0);
|
||||
wxASSERT(out != 0);
|
||||
|
||||
for (unsigned int i = 0U; i < length; i++) {
|
||||
out[i] = in[i] ^ SCRAMBLER_TABLE_BYTES[m_count++];
|
||||
|
||||
if (m_count >= SCRAMBLER_TABLE_BYTES_LENGTH)
|
||||
m_count = 0U;
|
||||
}
|
||||
}
|
||||
|
||||
void CDStarScrambler::reset()
|
||||
{
|
||||
m_count = 0U;
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (C) 2009,2013 by Jonathan Naylor, G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef DStarScrambler_H
|
||||
#define DStarScrambler_H
|
||||
|
||||
class CDStarScrambler {
|
||||
public:
|
||||
CDStarScrambler();
|
||||
~CDStarScrambler();
|
||||
|
||||
void process(bool* inOut, unsigned int length);
|
||||
void process(const bool* in, bool* out, unsigned int length);
|
||||
|
||||
void process(unsigned char* inOut, unsigned int length);
|
||||
void process(const unsigned char* in, unsigned char* out, unsigned int length);
|
||||
|
||||
void reset();
|
||||
|
||||
private:
|
||||
unsigned int m_count;
|
||||
};
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2014 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef DVAPController_H
|
||||
#define DVAPController_H
|
||||
|
||||
#define DVAP_DUMP
|
||||
|
||||
#include "SerialDataController.h"
|
||||
#include "RingBuffer.h"
|
||||
#include "HeaderData.h"
|
||||
#include "Modem.h"
|
||||
#include "Utils.h"
|
||||
|
||||
#include <wx/wx.h>
|
||||
|
||||
enum RESP_TYPE {
|
||||
RT_TIMEOUT,
|
||||
RT_ERROR,
|
||||
RT_UNKNOWN,
|
||||
RT_NAME,
|
||||
RT_SERIAL,
|
||||
RT_FIRMWARE,
|
||||
RT_START,
|
||||
RT_STOP,
|
||||
RT_MODULATION,
|
||||
RT_MODE,
|
||||
RT_SQUELCH,
|
||||
RT_POWER,
|
||||
RT_FREQUENCY,
|
||||
RT_FREQLIMITS,
|
||||
RT_STATE,
|
||||
RT_PTT,
|
||||
RT_ACK,
|
||||
RT_HEADER,
|
||||
RT_HEADER_ACK,
|
||||
RT_GMSK_DATA,
|
||||
RT_FM_DATA
|
||||
};
|
||||
|
||||
class CDVAPController : public CModem {
|
||||
public:
|
||||
CDVAPController(const wxString& port, unsigned int frequency, int power, int squelch);
|
||||
virtual ~CDVAPController();
|
||||
|
||||
virtual void* Entry();
|
||||
|
||||
virtual bool start();
|
||||
|
||||
virtual unsigned int getSpace();
|
||||
virtual bool isTXReady();
|
||||
|
||||
virtual bool getSquelch() const;
|
||||
virtual int getSignal() const;
|
||||
|
||||
virtual bool writeHeader(const CHeaderData& header);
|
||||
virtual bool writeData(const unsigned char* data, unsigned int length, bool end);
|
||||
|
||||
private:
|
||||
CSerialDataController m_serial;
|
||||
wxUint32 m_frequency;
|
||||
wxInt16 m_power;
|
||||
wxInt8 m_squelch;
|
||||
bool m_squelchOpen;
|
||||
int m_signal;
|
||||
unsigned char* m_buffer;
|
||||
wxUint16 m_streamId;
|
||||
wxUint8 m_framePos;
|
||||
wxUint8 m_seq;
|
||||
CRingBuffer<unsigned char> m_txData;
|
||||
#if defined(DVAP_DUMP)
|
||||
unsigned char** m_dvapData;
|
||||
unsigned int* m_dvapLength;
|
||||
unsigned int m_dvapIndex;
|
||||
#endif
|
||||
|
||||
bool getName();
|
||||
bool getFirmware();
|
||||
bool getSerial();
|
||||
|
||||
bool setModulation();
|
||||
bool setMode();
|
||||
|
||||
bool setSquelch();
|
||||
bool setPower();
|
||||
bool setFrequency();
|
||||
|
||||
bool startDVAP();
|
||||
bool stopDVAP();
|
||||
|
||||
void writePoll();
|
||||
|
||||
void resync();
|
||||
|
||||
RESP_TYPE getResponse(unsigned char* buffer, unsigned int& length);
|
||||
|
||||
#if defined(DVAP_DUMP)
|
||||
void storePacket(const unsigned char* data, unsigned int length);
|
||||
void dumpPackets();
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2015 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef DVMegaController_H
|
||||
#define DVMegaController_H
|
||||
|
||||
#include "SerialDataController.h"
|
||||
#include "RingBuffer.h"
|
||||
#include "Modem.h"
|
||||
#include "Utils.h"
|
||||
|
||||
#include <wx/wx.h>
|
||||
|
||||
enum RESP_TYPE_MEGA {
|
||||
RTM_TIMEOUT,
|
||||
RTM_ERROR,
|
||||
RTM_UNKNOWN,
|
||||
RTM_GET_STATUS,
|
||||
RTM_GET_VERSION,
|
||||
RTM_GET_SERIAL,
|
||||
RTM_GET_CONFIG,
|
||||
RTM_SET_CONFIG,
|
||||
RTM_RXPREAMBLE,
|
||||
RTM_START,
|
||||
RTM_HEADER,
|
||||
RTM_RXSYNC,
|
||||
RTM_DATA,
|
||||
RTM_EOT,
|
||||
RTM_RXLOST,
|
||||
RTM_SET_TESTMDE
|
||||
};
|
||||
|
||||
class CDVMegaController : public CModem {
|
||||
public:
|
||||
CDVMegaController(const wxString& port, const wxString& path, bool rxInvert, bool txInvert, unsigned int txDelay);
|
||||
CDVMegaController(const wxString& port, const wxString& path, unsigned int txDelay, unsigned int rxFrequency, unsigned int txFrequency, unsigned int power);
|
||||
virtual ~CDVMegaController();
|
||||
|
||||
virtual void* Entry();
|
||||
|
||||
virtual bool start();
|
||||
|
||||
virtual unsigned int getSpace();
|
||||
virtual bool isTXReady();
|
||||
|
||||
virtual bool writeHeader(const CHeaderData& header);
|
||||
virtual bool writeData(const unsigned char* data, unsigned int length, bool end);
|
||||
|
||||
virtual wxString getPath() const;
|
||||
|
||||
private:
|
||||
wxString m_port;
|
||||
wxString m_path;
|
||||
bool m_rxInvert;
|
||||
bool m_txInvert;
|
||||
unsigned int m_txDelay;
|
||||
unsigned int m_rxFrequency;
|
||||
unsigned int m_txFrequency;
|
||||
unsigned int m_power;
|
||||
CSerialDataController m_serial;
|
||||
unsigned char* m_buffer;
|
||||
CRingBuffer<unsigned char> m_txData;
|
||||
unsigned char m_txCounter;
|
||||
unsigned char m_pktCounter;
|
||||
bool m_rx;
|
||||
unsigned int m_txSpace;
|
||||
bool m_txEnabled;
|
||||
bool m_checksum;
|
||||
|
||||
bool readVersion();
|
||||
bool readStatus();
|
||||
bool setConfig();
|
||||
bool setFrequencyAndPower();
|
||||
bool setEnabled(bool enable);
|
||||
|
||||
RESP_TYPE_MEGA getResponse(unsigned char* buffer, unsigned int& length);
|
||||
|
||||
bool findPort();
|
||||
bool findPath();
|
||||
|
||||
bool findModem();
|
||||
bool openModem();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2014 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef DVRPTRV1Controller_H
|
||||
#define DVRPTRV1Controller_H
|
||||
|
||||
#include "SerialDataController.h"
|
||||
#include "RingBuffer.h"
|
||||
#include "Modem.h"
|
||||
#include "Utils.h"
|
||||
|
||||
#include <wx/wx.h>
|
||||
|
||||
enum RESP_TYPE_V1 {
|
||||
RT1_TIMEOUT,
|
||||
RT1_ERROR,
|
||||
RT1_UNKNOWN,
|
||||
RT1_GET_STATUS,
|
||||
RT1_GET_VERSION,
|
||||
RT1_GET_SERIAL,
|
||||
RT1_GET_CONFIG,
|
||||
RT1_SET_CONFIG,
|
||||
RT1_RXPREAMBLE,
|
||||
RT1_START,
|
||||
RT1_HEADER,
|
||||
RT1_RXSYNC,
|
||||
RT1_DATA,
|
||||
RT1_EOT,
|
||||
RT1_RXLOST,
|
||||
RT1_SET_TESTMDE
|
||||
};
|
||||
|
||||
class CDVRPTRV1Controller : public CModem {
|
||||
public:
|
||||
CDVRPTRV1Controller(const wxString& port, const wxString& path, bool rxInvert, bool txInvert, bool channel, unsigned int modLevel, unsigned int txDelay);
|
||||
virtual ~CDVRPTRV1Controller();
|
||||
|
||||
virtual void* Entry();
|
||||
|
||||
virtual bool start();
|
||||
|
||||
virtual unsigned int getSpace();
|
||||
virtual bool isTXReady();
|
||||
|
||||
virtual bool writeHeader(const CHeaderData& header);
|
||||
virtual bool writeData(const unsigned char* data, unsigned int length, bool end);
|
||||
|
||||
virtual wxString getPath() const;
|
||||
|
||||
private:
|
||||
wxString m_port;
|
||||
wxString m_path;
|
||||
bool m_rxInvert;
|
||||
bool m_txInvert;
|
||||
bool m_channel;
|
||||
unsigned int m_modLevel;
|
||||
unsigned int m_txDelay;
|
||||
CSerialDataController m_serial;
|
||||
unsigned char* m_buffer;
|
||||
CRingBuffer<unsigned char> m_txData;
|
||||
unsigned char m_txCounter;
|
||||
unsigned char m_pktCounter;
|
||||
bool m_rx;
|
||||
unsigned int m_txSpace;
|
||||
bool m_txEnabled;
|
||||
bool m_checksum;
|
||||
|
||||
bool readVersion();
|
||||
bool readStatus();
|
||||
bool setConfig();
|
||||
bool setEnabled(bool enable);
|
||||
|
||||
RESP_TYPE_V1 getResponse(unsigned char* buffer, unsigned int& length);
|
||||
|
||||
bool findPort();
|
||||
bool findPath();
|
||||
|
||||
bool findModem();
|
||||
bool openModem();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@ -0,0 +1,841 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2014 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "DVRPTRV2Controller.h"
|
||||
#include "DStarDefines.h"
|
||||
#include "Timer.h"
|
||||
|
||||
#if defined(__WINDOWS__)
|
||||
#include <setupapi.h>
|
||||
#else
|
||||
#include <wx/dir.h>
|
||||
#endif
|
||||
|
||||
const unsigned int MAX_RESPONSES = 30U;
|
||||
|
||||
const unsigned int BUFFER_LENGTH = 200U;
|
||||
|
||||
CDVRPTRV2Controller::CDVRPTRV2Controller(const wxString& port, const wxString& path, bool txInvert, unsigned int modLevel, bool duplex, const wxString& callsign, unsigned int txDelay) :
|
||||
CModem(),
|
||||
m_connection(CT_USB),
|
||||
m_usbPort(port),
|
||||
m_usbPath(path),
|
||||
m_address(),
|
||||
m_port(0U),
|
||||
m_txInvert(txInvert),
|
||||
m_modLevel(modLevel),
|
||||
m_duplex(duplex),
|
||||
m_callsign(callsign),
|
||||
m_txDelay(txDelay),
|
||||
m_usb(NULL),
|
||||
m_network(NULL),
|
||||
m_buffer(NULL),
|
||||
m_txData(1000U),
|
||||
m_rx(false)
|
||||
{
|
||||
wxASSERT(!port.IsEmpty());
|
||||
|
||||
m_usb = new CSerialDataController(port, SERIAL_115200);
|
||||
|
||||
m_buffer = new unsigned char[BUFFER_LENGTH];
|
||||
}
|
||||
|
||||
CDVRPTRV2Controller::CDVRPTRV2Controller(const wxString& address, unsigned int port, bool txInvert, unsigned int modLevel, bool duplex, const wxString& callsign, unsigned int txDelay) :
|
||||
CModem(),
|
||||
m_connection(CT_NETWORK),
|
||||
m_usbPort(),
|
||||
m_usbPath(),
|
||||
m_address(address),
|
||||
m_port(port),
|
||||
m_txInvert(txInvert),
|
||||
m_modLevel(modLevel),
|
||||
m_duplex(duplex),
|
||||
m_callsign(callsign),
|
||||
m_txDelay(txDelay),
|
||||
m_usb(NULL),
|
||||
m_network(NULL),
|
||||
m_buffer(NULL),
|
||||
m_txData(1000U),
|
||||
m_rx(false)
|
||||
{
|
||||
wxASSERT(!address.IsEmpty());
|
||||
wxASSERT(port > 0U);
|
||||
|
||||
m_network = new CTCPReaderWriter(address, port);
|
||||
|
||||
m_buffer = new unsigned char[BUFFER_LENGTH];
|
||||
}
|
||||
|
||||
CDVRPTRV2Controller::~CDVRPTRV2Controller()
|
||||
{
|
||||
delete m_usb;
|
||||
delete m_network;
|
||||
|
||||
delete[] m_buffer;
|
||||
}
|
||||
|
||||
bool CDVRPTRV2Controller::start()
|
||||
{
|
||||
findPort();
|
||||
|
||||
bool ret = openModem();
|
||||
if (!ret)
|
||||
return false;
|
||||
|
||||
findPath();
|
||||
|
||||
Create();
|
||||
SetPriority(100U);
|
||||
Run();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void* CDVRPTRV2Controller::Entry()
|
||||
{
|
||||
wxLogMessage(wxT("Starting DV-RPTR2 Modem Controller thread"));
|
||||
|
||||
// Clock every 5ms-ish
|
||||
CTimer pollTimer(200U, 0U, 250U);
|
||||
pollTimer.start();
|
||||
|
||||
unsigned int space = 0U;
|
||||
|
||||
while (!m_stopped) {
|
||||
// Poll the modem status every 250ms
|
||||
if (pollTimer.hasExpired()) {
|
||||
bool ret = readSpace();
|
||||
if (!ret) {
|
||||
ret = findModem();
|
||||
if (!ret) {
|
||||
wxLogMessage(wxT("Stopping DV-RPTR2 Modem Controller thread"));
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
pollTimer.start();
|
||||
}
|
||||
|
||||
unsigned int length;
|
||||
RESP_TYPE_V2 type = getResponse(m_buffer, length);
|
||||
|
||||
switch (type) {
|
||||
case RT2_TIMEOUT:
|
||||
break;
|
||||
|
||||
case RT2_ERROR: {
|
||||
bool ret = findModem();
|
||||
if (!ret) {
|
||||
wxLogMessage(wxT("Stopping DV-RPTR2 Modem Controller thread"));
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case RT2_HEADER: {
|
||||
// CUtils::dump(wxT("RT2_HEADER"), m_buffer, length);
|
||||
wxMutexLocker locker(m_mutex);
|
||||
|
||||
unsigned char data[2U];
|
||||
data[0U] = DSMTT_HEADER;
|
||||
data[1U] = RADIO_HEADER_LENGTH_BYTES;
|
||||
m_rxData.addData(data, 2U);
|
||||
|
||||
m_rxData.addData(m_buffer + 9U, RADIO_HEADER_LENGTH_BYTES - 2U);
|
||||
|
||||
// Dummy checksum
|
||||
data[0U] = 0xFFU;
|
||||
data[1U] = 0xFFU;
|
||||
m_rxData.addData(data, 2U);
|
||||
|
||||
data[0U] = DSMTT_DATA;
|
||||
data[1U] = DV_FRAME_LENGTH_BYTES;
|
||||
m_rxData.addData(data, 2U);
|
||||
|
||||
m_rxData.addData(m_buffer + 51U, DV_FRAME_LENGTH_BYTES);
|
||||
|
||||
m_rx = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case RT2_DATA: {
|
||||
// CUtils::dump(wxT("RT2_DATA"), m_buffer, length);
|
||||
wxMutexLocker locker(m_mutex);
|
||||
|
||||
unsigned char data[2U];
|
||||
data[0U] = DSMTT_DATA;
|
||||
data[1U] = DV_FRAME_LENGTH_BYTES;
|
||||
m_rxData.addData(data, 2U);
|
||||
|
||||
m_rxData.addData(m_buffer + 5U, DV_FRAME_LENGTH_BYTES);
|
||||
|
||||
m_rx = true;
|
||||
|
||||
// End of transmission?
|
||||
bool end = (m_buffer[19U] & 0x40U) == 0x40U;
|
||||
if (end) {
|
||||
data[0U] = DSMTT_EOT;
|
||||
data[1U] = 0U;
|
||||
m_rxData.addData(data, 2U);
|
||||
|
||||
m_rx = false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case RT2_SPACE:
|
||||
space = m_buffer[9U];
|
||||
// CUtils::dump(wxT("RT2_SPACE"), m_buffer, length);
|
||||
break;
|
||||
|
||||
// These should not be received in this loop, but don't complain if we do
|
||||
case RT2_QUERY:
|
||||
case RT2_CONFIG:
|
||||
break;
|
||||
|
||||
default:
|
||||
wxLogMessage(wxT("Unknown DV-RPTR2 message, type"));
|
||||
CUtils::dump(wxT("Buffer dump"), m_buffer, length);
|
||||
break;
|
||||
}
|
||||
|
||||
if (space >= 4U) {
|
||||
if (m_txData.hasData()) {
|
||||
unsigned char len = 0U;
|
||||
unsigned char data[200U];
|
||||
|
||||
{
|
||||
wxMutexLocker locker(m_mutex);
|
||||
|
||||
m_txData.getData(&len, 1U);
|
||||
m_txData.getData(data, len);
|
||||
}
|
||||
|
||||
// CUtils::dump(wxT("Write"), data, len);
|
||||
|
||||
bool ret = writeModem(data, len);
|
||||
if (!ret) {
|
||||
ret = findModem();
|
||||
if (!ret) {
|
||||
wxLogMessage(wxT("Stopping DV-RPTR2 Modem Controller thread"));
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
if (len > 100U)
|
||||
space -= 4U;
|
||||
else
|
||||
space--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Sleep(5UL);
|
||||
|
||||
pollTimer.clock();
|
||||
}
|
||||
|
||||
wxLogMessage(wxT("Stopping DV-RPTR2 Modem Controller thread"));
|
||||
|
||||
closeModem();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool CDVRPTRV2Controller::writeHeader(const CHeaderData& header)
|
||||
{
|
||||
bool ret = m_txData.hasSpace(106U);
|
||||
if (!ret) {
|
||||
wxLogWarning(wxT("No space to write the header"));
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned char buffer[105U];
|
||||
|
||||
::memset(buffer, 0x00U, 105U);
|
||||
|
||||
buffer[0U] = 'H';
|
||||
buffer[1U] = 'E';
|
||||
buffer[2U] = 'A';
|
||||
buffer[3U] = 'D';
|
||||
buffer[4U] = 'X';
|
||||
buffer[5U] = '0';
|
||||
buffer[6U] = '0';
|
||||
buffer[7U] = '0';
|
||||
buffer[8U] = '2';
|
||||
|
||||
::memset(buffer + 9U, ' ', RADIO_HEADER_LENGTH_BYTES);
|
||||
|
||||
buffer[9U] = header.getFlag1();
|
||||
buffer[10U] = header.getFlag2();
|
||||
buffer[11U] = header.getFlag3();
|
||||
|
||||
wxString rpt2 = header.getRptCall2();
|
||||
for (unsigned int i = 0U; i < rpt2.Len() && i < LONG_CALLSIGN_LENGTH; i++)
|
||||
buffer[i + 12U] = rpt2.GetChar(i);
|
||||
|
||||
wxString rpt1 = header.getRptCall1();
|
||||
for (unsigned int i = 0U; i < rpt1.Len() && i < LONG_CALLSIGN_LENGTH; i++)
|
||||
buffer[i + 20U] = rpt1.GetChar(i);
|
||||
|
||||
wxString your = header.getYourCall();
|
||||
for (unsigned int i = 0U; i < your.Len() && i < LONG_CALLSIGN_LENGTH; i++)
|
||||
buffer[i + 28U] = your.GetChar(i);
|
||||
|
||||
wxString my1 = header.getMyCall1();
|
||||
for (unsigned int i = 0U; i < my1.Len() && i < LONG_CALLSIGN_LENGTH; i++)
|
||||
buffer[i + 36U] = my1.GetChar(i);
|
||||
|
||||
wxString my2 = header.getMyCall2();
|
||||
for (unsigned int i = 0U; i < my2.Len() && i < SHORT_CALLSIGN_LENGTH; i++)
|
||||
buffer[i + 44U] = my2.GetChar(i);
|
||||
|
||||
wxMutexLocker locker(m_mutex);
|
||||
|
||||
unsigned char len = 105U;
|
||||
m_txData.addData(&len, 1U);
|
||||
|
||||
m_txData.addData(buffer, 105U);
|
||||
|
||||
m_tx = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDVRPTRV2Controller::writeData(const unsigned char* data, unsigned int, bool end)
|
||||
{
|
||||
bool ret = m_txData.hasSpace(18U);
|
||||
if (!ret) {
|
||||
wxLogWarning(wxT("No space to write data"));
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned char buffer[17U];
|
||||
|
||||
::memset(buffer, 0x00U, 17U);
|
||||
|
||||
buffer[0U] = 'H';
|
||||
buffer[1U] = 'E';
|
||||
buffer[2U] = 'A';
|
||||
buffer[3U] = 'D';
|
||||
buffer[4U] = 'Z';
|
||||
|
||||
::memcpy(buffer + 5U, data, DV_FRAME_LENGTH_BYTES);
|
||||
|
||||
if (end) {
|
||||
buffer[14U] = 0x55U;
|
||||
buffer[15U] = 0x55U;
|
||||
buffer[16U] = 0x55U;
|
||||
|
||||
m_tx = false;
|
||||
}
|
||||
|
||||
wxMutexLocker locker(m_mutex);
|
||||
|
||||
unsigned char len = 17U;
|
||||
m_txData.addData(&len, 1U);
|
||||
|
||||
m_txData.addData(buffer, 17U);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned int CDVRPTRV2Controller::getSpace()
|
||||
{
|
||||
return m_txData.freeSpace() / 18U;
|
||||
}
|
||||
|
||||
bool CDVRPTRV2Controller::isTXReady()
|
||||
{
|
||||
return m_txData.isEmpty();
|
||||
}
|
||||
|
||||
bool CDVRPTRV2Controller::readSerial()
|
||||
{
|
||||
unsigned char buffer[105U];
|
||||
|
||||
::memset(buffer, 0x00U, 105U);
|
||||
|
||||
buffer[0U] = 'H';
|
||||
buffer[1U] = 'E';
|
||||
buffer[2U] = 'A';
|
||||
buffer[3U] = 'D';
|
||||
buffer[4U] = 'X';
|
||||
buffer[5U] = '9';
|
||||
buffer[6U] = '0';
|
||||
buffer[7U] = '0';
|
||||
buffer[8U] = '0';
|
||||
|
||||
// CUtils::dump(wxT("Written"), buffer, 105U);
|
||||
|
||||
bool ret = writeModem(buffer, 105U);
|
||||
if (!ret)
|
||||
return false;
|
||||
|
||||
unsigned int count = 0U;
|
||||
unsigned int length;
|
||||
RESP_TYPE_V2 resp;
|
||||
do {
|
||||
::wxMilliSleep(10UL);
|
||||
|
||||
resp = getResponse(m_buffer, length);
|
||||
|
||||
if (resp != RT2_QUERY) {
|
||||
count++;
|
||||
if (count >= MAX_RESPONSES) {
|
||||
wxLogError(wxT("The DV-RPTR modem is not responding to the query command"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} while (resp != RT2_QUERY);
|
||||
|
||||
wxLogInfo(wxT("DV-RPTR Modem Hardware serial: 0x%02X%02X%02x%02X"), m_buffer[9U], m_buffer[10U], m_buffer[11U], m_buffer[12U]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDVRPTRV2Controller::readSpace()
|
||||
{
|
||||
unsigned char buffer[10U];
|
||||
|
||||
buffer[0U] = 'H';
|
||||
buffer[1U] = 'E';
|
||||
buffer[2U] = 'A';
|
||||
buffer[3U] = 'D';
|
||||
buffer[4U] = 'Y';
|
||||
buffer[5U] = '9';
|
||||
buffer[6U] = '0';
|
||||
buffer[7U] = '1';
|
||||
buffer[8U] = '1';
|
||||
buffer[9U] = 0x00U;
|
||||
|
||||
// CUtils::dump(wxT("Written"), buffer, 10U);
|
||||
|
||||
return writeModem(buffer, 10U);
|
||||
}
|
||||
|
||||
bool CDVRPTRV2Controller::setConfig()
|
||||
{
|
||||
unsigned char buffer[105U];
|
||||
|
||||
::memset(buffer, 0x00U, 105U);
|
||||
|
||||
buffer[0U] = 'H';
|
||||
buffer[1U] = 'E';
|
||||
buffer[2U] = 'A';
|
||||
buffer[3U] = 'D';
|
||||
buffer[4U] = 'X';
|
||||
buffer[5U] = '9';
|
||||
buffer[6U] = '0';
|
||||
buffer[7U] = '0';
|
||||
buffer[8U] = '1';
|
||||
|
||||
::memset(buffer + 9U, ' ', LONG_CALLSIGN_LENGTH);
|
||||
for (unsigned int i = 0U; i < LONG_CALLSIGN_LENGTH && i < m_callsign.Len(); i++)
|
||||
buffer[9U + i] = m_callsign.GetChar(i);
|
||||
|
||||
buffer[65U] = m_duplex ? 0x03U : 0x00U;
|
||||
|
||||
buffer[66U] = m_txInvert ? 0x01U : 0x00U;
|
||||
|
||||
buffer[73U] = (m_modLevel * 256U) / 100U;
|
||||
|
||||
// Internal and external speaker on
|
||||
buffer[86U] = 0x03U;
|
||||
|
||||
buffer[87U] = 0x01U;
|
||||
|
||||
if (m_txDelay < 100U)
|
||||
m_txDelay = 100U;
|
||||
if (m_txDelay > 850U)
|
||||
m_txDelay = 850U;
|
||||
|
||||
buffer[89U] = m_txDelay / 10U;
|
||||
|
||||
// CUtils::dump(wxT("Written"), buffer, 105U);
|
||||
|
||||
bool ret = writeModem(buffer, 105U);
|
||||
if (!ret)
|
||||
return false;
|
||||
|
||||
unsigned int count = 0U;
|
||||
unsigned int length;
|
||||
RESP_TYPE_V2 resp;
|
||||
do {
|
||||
::wxMilliSleep(10UL);
|
||||
|
||||
resp = getResponse(m_buffer, length);
|
||||
|
||||
if (resp != RT2_CONFIG) {
|
||||
count++;
|
||||
if (count >= MAX_RESPONSES) {
|
||||
wxLogError(wxT("The DV-RPTR modem is not responding to the configure command"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} while (resp != RT2_CONFIG);
|
||||
|
||||
// CUtils::dump(wxT("Response"), m_buffer, length);
|
||||
|
||||
wxString firmware((char*)(m_buffer + 9U), wxConvLocal);
|
||||
|
||||
wxLogInfo(wxT("DV-RPTR Modem Firmware version: %s"), firmware.c_str());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
RESP_TYPE_V2 CDVRPTRV2Controller::getResponse(unsigned char *buffer, unsigned int& length)
|
||||
{
|
||||
// Get the start of the frame or nothing at all
|
||||
int ret = readModem(buffer + 0U, 5U);
|
||||
if (ret < 0) {
|
||||
wxLogError(wxT("Error when reading from the DV-RPTR"));
|
||||
return RT2_ERROR;
|
||||
}
|
||||
|
||||
if (ret == 0)
|
||||
return RT2_TIMEOUT;
|
||||
|
||||
while (::memcmp(buffer + 0U, "HEAD", 4U) != 0) {
|
||||
buffer[0U] = buffer[1U];
|
||||
buffer[1U] = buffer[2U];
|
||||
buffer[2U] = buffer[3U];
|
||||
buffer[3U] = buffer[4U];
|
||||
|
||||
ret = readModem(buffer + 4U, 1U);
|
||||
if (ret < 0) {
|
||||
wxLogError(wxT("Error when reading from the DV-RPTR"));
|
||||
return RT2_ERROR;
|
||||
}
|
||||
|
||||
if (ret == 0)
|
||||
return RT2_TIMEOUT;
|
||||
}
|
||||
|
||||
switch (buffer[4U]) {
|
||||
case 'X':
|
||||
length = 105U;
|
||||
break;
|
||||
case 'Y':
|
||||
length = 10U;
|
||||
break;
|
||||
case 'Z':
|
||||
length = 20U;
|
||||
break;
|
||||
default:
|
||||
wxLogError(wxT("DV-RPTR frame type is incorrect - 0x%02X"), buffer[4U]);
|
||||
return RT2_UNKNOWN;
|
||||
}
|
||||
|
||||
unsigned int offset = 5U;
|
||||
|
||||
while (offset < length) {
|
||||
ret = readModem(buffer + offset, length - offset);
|
||||
if (ret < 0) {
|
||||
wxLogError(wxT("Error when reading from the DV-RPTR"));
|
||||
return RT2_ERROR;
|
||||
}
|
||||
|
||||
if (ret > 0)
|
||||
offset += ret;
|
||||
|
||||
if (ret == 0)
|
||||
Sleep(5UL);
|
||||
}
|
||||
|
||||
// CUtils::dump(wxT("Received"), buffer, length);
|
||||
|
||||
if (::memcmp(buffer + 0U, "HEADZ", 5U) == 0) {
|
||||
return RT2_DATA;
|
||||
} else if (::memcmp(buffer + 5U, "0001", 4U) == 0) {
|
||||
if (buffer[104U] == 0x01U)
|
||||
return RT2_HEADER;
|
||||
} else if (::memcmp(buffer + 5U, "9900", 4U) == 0) {
|
||||
return RT2_QUERY;
|
||||
} else if (::memcmp(buffer + 5U, "9001", 4U) == 0) {
|
||||
return RT2_CONFIG;
|
||||
} else if (::memcmp(buffer + 5U, "9011", 4U) == 0) {
|
||||
return RT2_SPACE;
|
||||
} else if (::memcmp(buffer + 5U, "9021", 4U) == 0) {
|
||||
return RT2_TIMEOUT;
|
||||
}
|
||||
|
||||
return RT2_UNKNOWN;
|
||||
}
|
||||
|
||||
wxString CDVRPTRV2Controller::getPath() const
|
||||
{
|
||||
return m_usbPath;
|
||||
}
|
||||
|
||||
bool CDVRPTRV2Controller::findPort()
|
||||
{
|
||||
if (m_connection != CT_USB)
|
||||
return true;
|
||||
|
||||
if (m_usbPath.IsEmpty())
|
||||
return false;
|
||||
|
||||
#if defined(__WINDOWS__)
|
||||
#else
|
||||
wxDir dir;
|
||||
bool ret1 = dir.Open(wxT("/sys/class/tty"));
|
||||
if (!ret1) {
|
||||
wxLogError(wxT("Cannot open directory /sys/class/tty"));
|
||||
return false;
|
||||
}
|
||||
|
||||
wxString fileName;
|
||||
ret1 = dir.GetFirst(&fileName, wxT("ttyACM*"));
|
||||
while (ret1) {
|
||||
wxString path;
|
||||
path.Printf(wxT("/sys/class/tty/%s"), fileName.c_str());
|
||||
|
||||
char cpath[255U];
|
||||
::memset(cpath, 0x00U, 255U);
|
||||
|
||||
for (unsigned int i = 0U; i < path.Len(); i++)
|
||||
cpath[i] = path.GetChar(i);
|
||||
|
||||
char symlink[255U];
|
||||
int ret2 = ::readlink(cpath, symlink, 255U);
|
||||
if (ret2 < 0) {
|
||||
::strcat(cpath, "/device");
|
||||
ret2 = ::readlink(cpath, symlink, 255U);
|
||||
if (ret2 < 0) {
|
||||
wxLogError(wxT("Error from readlink()"));
|
||||
return false;
|
||||
}
|
||||
|
||||
path = wxString(symlink, wxConvLocal, ret2);
|
||||
} else {
|
||||
// Get all but the last section
|
||||
wxString fullPath = wxString(symlink, wxConvLocal, ret2);
|
||||
path = fullPath.BeforeLast(wxT('/'));
|
||||
}
|
||||
|
||||
if (path.IsSameAs(m_usbPath)) {
|
||||
m_usbPort.Printf(wxT("/dev/%s"), fileName.c_str());
|
||||
|
||||
wxLogMessage(wxT("Found modem port of %s based on the path"), m_usbPort.c_str());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ret1 = dir.GetNext(&fileName);
|
||||
}
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CDVRPTRV2Controller::findPath()
|
||||
{
|
||||
if (m_connection != CT_USB)
|
||||
return true;
|
||||
|
||||
#if defined(__WINDOWS__)
|
||||
#ifdef notdef
|
||||
GUID guids[5U];
|
||||
|
||||
DWORD count;
|
||||
BOOL res = ::SetupDiClassGuidsFromName(L"Multifunction", guids, 5U, &count);
|
||||
if (!res) {
|
||||
wxLogError(wxT("Error from SetupDiClassGuidsFromName: err=%u"), ::GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
for (DWORD i = 0U; i < count; i++) {
|
||||
HDEVINFO devInfo = ::SetupDiGetClassDevs(&guids[i], NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
|
||||
if (devInfo == INVALID_HANDLE_VALUE) {
|
||||
wxLogError(wxT("Error from SetupDiGetClassDevs: err=%u"), ::GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
SP_DEVICE_INTERFACE_DATA devInfoData;
|
||||
devInfoData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
|
||||
|
||||
for (DWORD index = 0U; ::SetupDiEnumDeviceInterfaces(devInfo, NULL, &guids[i], index, &devInfoData); index++) {
|
||||
// Find the required length of the device structure
|
||||
DWORD length;
|
||||
::SetupDiGetDeviceInterfaceDetail(devInfo, &devInfoData, NULL, 0U, &length, NULL);
|
||||
|
||||
PSP_DEVICE_INTERFACE_DETAIL_DATA detailData = PSP_DEVICE_INTERFACE_DETAIL_DATA(::malloc(length));
|
||||
detailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
|
||||
|
||||
// Get the detailed data into the newly allocated device structure
|
||||
DWORD required;
|
||||
res = ::SetupDiGetDeviceInterfaceDetail(devInfo, &devInfoData, detailData, length, &required, NULL);
|
||||
if (!res) {
|
||||
wxLogError(wxT("Error from SetupDiGetDeviceInterfaceDetail: err=%u"), ::GetLastError());
|
||||
::SetupDiDestroyDeviceInfoList(devInfo);
|
||||
::free(detailData);
|
||||
return false;
|
||||
}
|
||||
|
||||
::free(detailData);
|
||||
}
|
||||
|
||||
::SetupDiDestroyDeviceInfoList(devInfo);
|
||||
}
|
||||
|
||||
return false;
|
||||
#endif
|
||||
#else
|
||||
wxString path;
|
||||
path.Printf(wxT("/sys/class/tty/%s"), m_usbPort.Mid(5U).c_str());
|
||||
|
||||
char cpath[255U];
|
||||
::memset(cpath, 0x00U, 255U);
|
||||
|
||||
for (unsigned int i = 0U; i < path.Len(); i++)
|
||||
cpath[i] = path.GetChar(i);
|
||||
|
||||
char symlink[255U];
|
||||
int ret = ::readlink(cpath, symlink, 255U);
|
||||
if (ret < 0) {
|
||||
::strcat(cpath, "/device");
|
||||
ret = ::readlink(cpath, symlink, 255U);
|
||||
if (ret < 0) {
|
||||
wxLogError(wxT("Error from readlink()"));
|
||||
return false;
|
||||
}
|
||||
|
||||
path = wxString(symlink, wxConvLocal, ret);
|
||||
} else {
|
||||
wxString fullPath = wxString(symlink, wxConvLocal, ret);
|
||||
path = fullPath.BeforeLast(wxT('/'));
|
||||
}
|
||||
|
||||
if (m_usbPath.IsEmpty())
|
||||
wxLogMessage(wxT("Found modem path of %s"), path.c_str());
|
||||
|
||||
m_usbPath = path;
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDVRPTRV2Controller::findModem()
|
||||
{
|
||||
closeModem();
|
||||
|
||||
// Tell the repeater that the signal has gone away
|
||||
if (m_rx) {
|
||||
wxMutexLocker locker(m_mutex);
|
||||
|
||||
unsigned char data[2U];
|
||||
data[0U] = DSMTT_EOT;
|
||||
data[1U] = 0U;
|
||||
m_rxData.addData(data, 2U);
|
||||
|
||||
m_rx = false;
|
||||
}
|
||||
|
||||
unsigned int count = 0U;
|
||||
|
||||
// Purge the transmit buffer every 500ms to avoid overflow, but only try and reopen the modem every 2s
|
||||
while (!m_stopped) {
|
||||
count++;
|
||||
if (count >= 4U) {
|
||||
wxLogMessage(wxT("Trying to reopen the modem"));
|
||||
|
||||
bool ret = findPort();
|
||||
if (ret) {
|
||||
ret = openModem();
|
||||
if (ret)
|
||||
return true;
|
||||
}
|
||||
|
||||
count = 0U;
|
||||
}
|
||||
|
||||
Sleep(500UL);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CDVRPTRV2Controller::openModem()
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
switch (m_connection) {
|
||||
case CT_USB:
|
||||
ret = m_usb->open();
|
||||
break;
|
||||
case CT_NETWORK:
|
||||
ret = m_network->open();
|
||||
break;
|
||||
default:
|
||||
wxLogError(wxT("Invalid connection type: %d"), int(m_connection));
|
||||
break;
|
||||
}
|
||||
|
||||
if (!ret)
|
||||
return false;
|
||||
|
||||
ret = readSerial();
|
||||
if (!ret) {
|
||||
closeModem();
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = setConfig();
|
||||
if (!ret) {
|
||||
closeModem();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int CDVRPTRV2Controller::readModem(unsigned char* buffer, unsigned int length)
|
||||
{
|
||||
switch (m_connection) {
|
||||
case CT_USB:
|
||||
return m_usb->read(buffer, length);
|
||||
case CT_NETWORK:
|
||||
return m_network->read(buffer, length, 0U);
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
bool CDVRPTRV2Controller::writeModem(const unsigned char* buffer, unsigned int length)
|
||||
{
|
||||
switch (m_connection) {
|
||||
case CT_USB:
|
||||
return m_usb->write(buffer, length) == int(length);
|
||||
case CT_NETWORK:
|
||||
return m_network->write(buffer, length);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void CDVRPTRV2Controller::closeModem()
|
||||
{
|
||||
switch (m_connection) {
|
||||
case CT_USB:
|
||||
return m_usb->close();
|
||||
case CT_NETWORK:
|
||||
return m_network->close();
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2014 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef DVRPTRV2Controller_H
|
||||
#define DVRPTRV2Controller_H
|
||||
|
||||
#include "SerialDataController.h"
|
||||
#include "TCPReaderWriter.h"
|
||||
#include "DStarDefines.h"
|
||||
#include "RingBuffer.h"
|
||||
#include "Modem.h"
|
||||
#include "Utils.h"
|
||||
|
||||
#include <wx/wx.h>
|
||||
|
||||
enum RESP_TYPE_V2 {
|
||||
RT2_TIMEOUT,
|
||||
RT2_ERROR,
|
||||
RT2_UNKNOWN,
|
||||
RT2_SPACE,
|
||||
RT2_QUERY,
|
||||
RT2_CONFIG,
|
||||
RT2_HEADER,
|
||||
RT2_DATA
|
||||
};
|
||||
|
||||
class CDVRPTRV2Controller : public CModem {
|
||||
public:
|
||||
CDVRPTRV2Controller(const wxString& port, const wxString& path, bool txInvert, unsigned int modLevel, bool duplex, const wxString& callsign, unsigned int txDelay);
|
||||
CDVRPTRV2Controller(const wxString& address, unsigned int port, bool txInvert, unsigned int modLevel, bool duplex, const wxString& callsign, unsigned int txDelay);
|
||||
virtual ~CDVRPTRV2Controller();
|
||||
|
||||
virtual void* Entry();
|
||||
|
||||
virtual bool start();
|
||||
|
||||
virtual unsigned int getSpace();
|
||||
virtual bool isTXReady();
|
||||
|
||||
virtual bool writeHeader(const CHeaderData& header);
|
||||
virtual bool writeData(const unsigned char* data, unsigned int length, bool end);
|
||||
|
||||
virtual wxString getPath() const;
|
||||
|
||||
private:
|
||||
CONNECTION_TYPE m_connection;
|
||||
wxString m_usbPort;
|
||||
wxString m_usbPath;
|
||||
wxString m_address;
|
||||
unsigned int m_port;
|
||||
bool m_txInvert;
|
||||
unsigned int m_modLevel;
|
||||
bool m_duplex;
|
||||
wxString m_callsign;
|
||||
unsigned int m_txDelay;
|
||||
CSerialDataController* m_usb;
|
||||
CTCPReaderWriter* m_network;
|
||||
unsigned char* m_buffer;
|
||||
CRingBuffer<unsigned char> m_txData;
|
||||
bool m_rx;
|
||||
|
||||
bool readSerial();
|
||||
bool setConfig();
|
||||
bool readSpace();
|
||||
|
||||
RESP_TYPE_V2 getResponse(unsigned char* buffer, unsigned int& length);
|
||||
|
||||
bool findPort();
|
||||
bool findPath();
|
||||
|
||||
bool findModem();
|
||||
bool openModem();
|
||||
|
||||
int readModem(unsigned char* buffer, unsigned int length);
|
||||
bool writeModem(const unsigned char* buffer, unsigned int length);
|
||||
void closeModem();
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,841 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2014 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "DVRPTRV3Controller.h"
|
||||
#include "DStarDefines.h"
|
||||
#include "Timer.h"
|
||||
|
||||
#if defined(__WINDOWS__)
|
||||
#include <setupapi.h>
|
||||
#else
|
||||
#include <wx/dir.h>
|
||||
#endif
|
||||
|
||||
const unsigned int MAX_RESPONSES = 30U;
|
||||
|
||||
const unsigned int BUFFER_LENGTH = 200U;
|
||||
|
||||
CDVRPTRV3Controller::CDVRPTRV3Controller(const wxString& port, const wxString& path, bool txInvert, unsigned int modLevel, bool duplex, const wxString& callsign, unsigned int txDelay) :
|
||||
CModem(),
|
||||
m_connection(CT_USB),
|
||||
m_usbPort(port),
|
||||
m_usbPath(path),
|
||||
m_address(),
|
||||
m_port(0U),
|
||||
m_txInvert(txInvert),
|
||||
m_modLevel(modLevel),
|
||||
m_duplex(duplex),
|
||||
m_callsign(callsign),
|
||||
m_txDelay(txDelay),
|
||||
m_usb(NULL),
|
||||
m_network(NULL),
|
||||
m_buffer(NULL),
|
||||
m_txData(1000U),
|
||||
m_rx(false)
|
||||
{
|
||||
wxASSERT(!port.IsEmpty());
|
||||
|
||||
m_usb = new CSerialDataController(port, SERIAL_115200);
|
||||
|
||||
m_buffer = new unsigned char[BUFFER_LENGTH];
|
||||
}
|
||||
|
||||
CDVRPTRV3Controller::CDVRPTRV3Controller(const wxString& address, unsigned int port, bool txInvert, unsigned int modLevel, bool duplex, const wxString& callsign, unsigned int txDelay) :
|
||||
CModem(),
|
||||
m_connection(CT_NETWORK),
|
||||
m_usbPort(),
|
||||
m_usbPath(),
|
||||
m_address(address),
|
||||
m_port(port),
|
||||
m_txInvert(txInvert),
|
||||
m_modLevel(modLevel),
|
||||
m_duplex(duplex),
|
||||
m_callsign(callsign),
|
||||
m_txDelay(txDelay),
|
||||
m_usb(NULL),
|
||||
m_network(NULL),
|
||||
m_buffer(NULL),
|
||||
m_txData(1000U),
|
||||
m_rx(false)
|
||||
{
|
||||
wxASSERT(!address.IsEmpty());
|
||||
wxASSERT(port > 0U);
|
||||
|
||||
m_network = new CTCPReaderWriter(address, port);
|
||||
|
||||
m_buffer = new unsigned char[BUFFER_LENGTH];
|
||||
}
|
||||
|
||||
CDVRPTRV3Controller::~CDVRPTRV3Controller()
|
||||
{
|
||||
delete m_usb;
|
||||
delete m_network;
|
||||
|
||||
delete[] m_buffer;
|
||||
}
|
||||
|
||||
bool CDVRPTRV3Controller::start()
|
||||
{
|
||||
findPort();
|
||||
|
||||
bool ret = openModem();
|
||||
if (!ret)
|
||||
return false;
|
||||
|
||||
findPath();
|
||||
|
||||
Create();
|
||||
SetPriority(100U);
|
||||
Run();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void* CDVRPTRV3Controller::Entry()
|
||||
{
|
||||
wxLogMessage(wxT("Starting DV-RPTR3 Modem Controller thread"));
|
||||
|
||||
// Clock every 5ms-ish
|
||||
CTimer pollTimer(200U, 0U, 250U);
|
||||
pollTimer.start();
|
||||
|
||||
unsigned int space = 0U;
|
||||
|
||||
while (!m_stopped) {
|
||||
// Poll the modem status every 250ms
|
||||
if (pollTimer.hasExpired()) {
|
||||
bool ret = readSpace();
|
||||
if (!ret) {
|
||||
ret = findModem();
|
||||
if (!ret) {
|
||||
wxLogMessage(wxT("Stopping DV-RPTR3 Modem Controller thread"));
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
pollTimer.start();
|
||||
}
|
||||
|
||||
unsigned int length;
|
||||
RESP_TYPE_V3 type = getResponse(m_buffer, length);
|
||||
|
||||
switch (type) {
|
||||
case RT3_TIMEOUT:
|
||||
break;
|
||||
|
||||
case RT3_ERROR: {
|
||||
bool ret = findModem();
|
||||
if (!ret) {
|
||||
wxLogMessage(wxT("Stopping DV-RPTR3 Modem Controller thread"));
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case RT3_HEADER: {
|
||||
// CUtils::dump(wxT("RT3_HEADER"), m_buffer, length);
|
||||
wxMutexLocker locker(m_mutex);
|
||||
|
||||
unsigned char data[2U];
|
||||
data[0U] = DSMTT_HEADER;
|
||||
data[1U] = RADIO_HEADER_LENGTH_BYTES;
|
||||
m_rxData.addData(data, 2U);
|
||||
|
||||
m_rxData.addData(m_buffer + 9U, RADIO_HEADER_LENGTH_BYTES - 2U);
|
||||
|
||||
// Dummy checksum
|
||||
data[0U] = 0xFFU;
|
||||
data[1U] = 0xFFU;
|
||||
m_rxData.addData(data, 2U);
|
||||
|
||||
data[0U] = DSMTT_DATA;
|
||||
data[1U] = DV_FRAME_LENGTH_BYTES;
|
||||
m_rxData.addData(data, 2U);
|
||||
|
||||
m_rxData.addData(m_buffer + 51U, DV_FRAME_LENGTH_BYTES);
|
||||
|
||||
m_rx = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case RT3_DATA: {
|
||||
// CUtils::dump(wxT("RT3_DATA"), m_buffer, length);
|
||||
wxMutexLocker locker(m_mutex);
|
||||
|
||||
unsigned char data[2U];
|
||||
data[0U] = DSMTT_DATA;
|
||||
data[1U] = DV_FRAME_LENGTH_BYTES;
|
||||
m_rxData.addData(data, 2U);
|
||||
|
||||
m_rxData.addData(m_buffer + 5U, DV_FRAME_LENGTH_BYTES);
|
||||
|
||||
m_rx = true;
|
||||
|
||||
// End of transmission?
|
||||
bool end = (m_buffer[19U] & 0x40U) == 0x40U;
|
||||
if (end) {
|
||||
data[0U] = DSMTT_EOT;
|
||||
data[1U] = 0U;
|
||||
m_rxData.addData(data, 2U);
|
||||
|
||||
m_rx = false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case RT3_SPACE:
|
||||
space = m_buffer[9U];
|
||||
// CUtils::dump(wxT("RT3_SPACE"), m_buffer, length);
|
||||
break;
|
||||
|
||||
// These should not be received in this loop, but don't complain if we do
|
||||
case RT3_QUERY:
|
||||
case RT3_CONFIG:
|
||||
break;
|
||||
|
||||
default:
|
||||
wxLogMessage(wxT("Unknown DV-RPTR3 message, type"));
|
||||
CUtils::dump(wxT("Buffer dump"), m_buffer, length);
|
||||
break;
|
||||
}
|
||||
|
||||
if (space >= 4U) {
|
||||
if (m_txData.hasData()) {
|
||||
unsigned char len = 0U;
|
||||
unsigned char data[200U];
|
||||
|
||||
{
|
||||
wxMutexLocker locker(m_mutex);
|
||||
|
||||
m_txData.getData(&len, 1U);
|
||||
m_txData.getData(data, len);
|
||||
}
|
||||
|
||||
// CUtils::dump(wxT("Write"), data, len);
|
||||
|
||||
bool ret = writeModem(data, len);
|
||||
if (!ret) {
|
||||
ret = findModem();
|
||||
if (!ret) {
|
||||
wxLogMessage(wxT("Stopping DV-RPTR3 Modem Controller thread"));
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
if (len > 100U)
|
||||
space -= 4U;
|
||||
else
|
||||
space--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Sleep(5UL);
|
||||
|
||||
pollTimer.clock();
|
||||
}
|
||||
|
||||
wxLogMessage(wxT("Stopping DV-RPTR3 Modem Controller thread"));
|
||||
|
||||
closeModem();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool CDVRPTRV3Controller::writeHeader(const CHeaderData& header)
|
||||
{
|
||||
bool ret = m_txData.hasSpace(106U);
|
||||
if (!ret) {
|
||||
wxLogWarning(wxT("No space to write the header"));
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned char buffer[105U];
|
||||
|
||||
::memset(buffer, 0x00U, 105U);
|
||||
|
||||
buffer[0U] = 'H';
|
||||
buffer[1U] = 'E';
|
||||
buffer[2U] = 'A';
|
||||
buffer[3U] = 'D';
|
||||
buffer[4U] = 'X';
|
||||
buffer[5U] = '0';
|
||||
buffer[6U] = '0';
|
||||
buffer[7U] = '0';
|
||||
buffer[8U] = '2';
|
||||
|
||||
::memset(buffer + 9U, ' ', RADIO_HEADER_LENGTH_BYTES);
|
||||
|
||||
buffer[9U] = header.getFlag1();
|
||||
buffer[10U] = header.getFlag2();
|
||||
buffer[11U] = header.getFlag3();
|
||||
|
||||
wxString rpt2 = header.getRptCall2();
|
||||
for (unsigned int i = 0U; i < rpt2.Len() && i < LONG_CALLSIGN_LENGTH; i++)
|
||||
buffer[i + 12U] = rpt2.GetChar(i);
|
||||
|
||||
wxString rpt1 = header.getRptCall1();
|
||||
for (unsigned int i = 0U; i < rpt1.Len() && i < LONG_CALLSIGN_LENGTH; i++)
|
||||
buffer[i + 20U] = rpt1.GetChar(i);
|
||||
|
||||
wxString your = header.getYourCall();
|
||||
for (unsigned int i = 0U; i < your.Len() && i < LONG_CALLSIGN_LENGTH; i++)
|
||||
buffer[i + 28U] = your.GetChar(i);
|
||||
|
||||
wxString my1 = header.getMyCall1();
|
||||
for (unsigned int i = 0U; i < my1.Len() && i < LONG_CALLSIGN_LENGTH; i++)
|
||||
buffer[i + 36U] = my1.GetChar(i);
|
||||
|
||||
wxString my2 = header.getMyCall2();
|
||||
for (unsigned int i = 0U; i < my2.Len() && i < SHORT_CALLSIGN_LENGTH; i++)
|
||||
buffer[i + 44U] = my2.GetChar(i);
|
||||
|
||||
wxMutexLocker locker(m_mutex);
|
||||
|
||||
unsigned char len = 105U;
|
||||
m_txData.addData(&len, 1U);
|
||||
|
||||
m_txData.addData(buffer, 105U);
|
||||
|
||||
m_tx = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDVRPTRV3Controller::writeData(const unsigned char* data, unsigned int, bool end)
|
||||
{
|
||||
bool ret = m_txData.hasSpace(18U);
|
||||
if (!ret) {
|
||||
wxLogWarning(wxT("No space to write data"));
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned char buffer[17U];
|
||||
|
||||
::memset(buffer, 0x00U, 17U);
|
||||
|
||||
buffer[0U] = 'H';
|
||||
buffer[1U] = 'E';
|
||||
buffer[2U] = 'A';
|
||||
buffer[3U] = 'D';
|
||||
buffer[4U] = 'Z';
|
||||
|
||||
::memcpy(buffer + 5U, data, DV_FRAME_LENGTH_BYTES);
|
||||
|
||||
if (end) {
|
||||
buffer[14U] = 0x55U;
|
||||
buffer[15U] = 0x55U;
|
||||
buffer[16U] = 0x55U;
|
||||
|
||||
m_tx = false;
|
||||
}
|
||||
|
||||
wxMutexLocker locker(m_mutex);
|
||||
|
||||
unsigned char len = 17U;
|
||||
m_txData.addData(&len, 1U);
|
||||
|
||||
m_txData.addData(buffer, 17U);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned int CDVRPTRV3Controller::getSpace()
|
||||
{
|
||||
return m_txData.freeSpace() / 18U;
|
||||
}
|
||||
|
||||
bool CDVRPTRV3Controller::isTXReady()
|
||||
{
|
||||
return m_txData.isEmpty();
|
||||
}
|
||||
|
||||
bool CDVRPTRV3Controller::readSerial()
|
||||
{
|
||||
unsigned char buffer[105U];
|
||||
|
||||
::memset(buffer, 0x00U, 105U);
|
||||
|
||||
buffer[0U] = 'H';
|
||||
buffer[1U] = 'E';
|
||||
buffer[2U] = 'A';
|
||||
buffer[3U] = 'D';
|
||||
buffer[4U] = 'X';
|
||||
buffer[5U] = '9';
|
||||
buffer[6U] = '0';
|
||||
buffer[7U] = '0';
|
||||
buffer[8U] = '0';
|
||||
|
||||
// CUtils::dump(wxT("Written"), buffer, 105U);
|
||||
|
||||
bool ret = writeModem(buffer, 105U);
|
||||
if (!ret)
|
||||
return false;
|
||||
|
||||
unsigned int count = 0U;
|
||||
unsigned int length;
|
||||
RESP_TYPE_V3 resp;
|
||||
do {
|
||||
::wxMilliSleep(10UL);
|
||||
|
||||
resp = getResponse(m_buffer, length);
|
||||
|
||||
if (resp != RT3_QUERY) {
|
||||
count++;
|
||||
if (count >= MAX_RESPONSES) {
|
||||
wxLogError(wxT("The DV-RPTR modem is not responding to the query command"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} while (resp != RT3_QUERY);
|
||||
|
||||
wxLogInfo(wxT("DV-RPTR Modem Hardware serial: 0x%02X%02X%02x%02X"), m_buffer[9U], m_buffer[10U], m_buffer[11U], m_buffer[12U]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDVRPTRV3Controller::readSpace()
|
||||
{
|
||||
unsigned char buffer[10U];
|
||||
|
||||
buffer[0U] = 'H';
|
||||
buffer[1U] = 'E';
|
||||
buffer[2U] = 'A';
|
||||
buffer[3U] = 'D';
|
||||
buffer[4U] = 'Y';
|
||||
buffer[5U] = '9';
|
||||
buffer[6U] = '0';
|
||||
buffer[7U] = '1';
|
||||
buffer[8U] = '1';
|
||||
buffer[9U] = 0x00U;
|
||||
|
||||
// CUtils::dump(wxT("Written"), buffer, 10U);
|
||||
|
||||
return writeModem(buffer, 10U);
|
||||
}
|
||||
|
||||
bool CDVRPTRV3Controller::setConfig()
|
||||
{
|
||||
unsigned char buffer[105U];
|
||||
|
||||
::memset(buffer, 0x00U, 105U);
|
||||
|
||||
buffer[0U] = 'H';
|
||||
buffer[1U] = 'E';
|
||||
buffer[2U] = 'A';
|
||||
buffer[3U] = 'D';
|
||||
buffer[4U] = 'X';
|
||||
buffer[5U] = '9';
|
||||
buffer[6U] = '0';
|
||||
buffer[7U] = '0';
|
||||
buffer[8U] = '1';
|
||||
|
||||
::memset(buffer + 9U, ' ', LONG_CALLSIGN_LENGTH);
|
||||
for (unsigned int i = 0U; i < LONG_CALLSIGN_LENGTH && i < m_callsign.Len(); i++)
|
||||
buffer[9U + i] = m_callsign.GetChar(i);
|
||||
|
||||
buffer[65U] = m_duplex ? 0x03U : 0x00U;
|
||||
|
||||
buffer[66U] = m_txInvert ? 0x01U : 0x00U;
|
||||
|
||||
buffer[73U] = (m_modLevel * 256U) / 100U;
|
||||
|
||||
// Internal and external speaker on
|
||||
buffer[86U] = 0x03U;
|
||||
|
||||
buffer[87U] = 0x01U;
|
||||
|
||||
if (m_txDelay < 100U)
|
||||
m_txDelay = 100U;
|
||||
if (m_txDelay > 850U)
|
||||
m_txDelay = 850U;
|
||||
|
||||
buffer[89U] = m_txDelay / 10U;
|
||||
|
||||
// CUtils::dump(wxT("Written"), buffer, 105U);
|
||||
|
||||
bool ret = writeModem(buffer, 105U);
|
||||
if (!ret)
|
||||
return false;
|
||||
|
||||
unsigned int count = 0U;
|
||||
unsigned int length;
|
||||
RESP_TYPE_V3 resp;
|
||||
do {
|
||||
::wxMilliSleep(10UL);
|
||||
|
||||
resp = getResponse(m_buffer, length);
|
||||
|
||||
if (resp != RT3_CONFIG) {
|
||||
count++;
|
||||
if (count >= MAX_RESPONSES) {
|
||||
wxLogError(wxT("The DV-RPTR modem is not responding to the configure command"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} while (resp != RT3_CONFIG);
|
||||
|
||||
// CUtils::dump(wxT("Response"), m_buffer, length);
|
||||
|
||||
wxString firmware((char*)(m_buffer + 9U), wxConvLocal);
|
||||
|
||||
wxLogInfo(wxT("DV-RPTR Modem Firmware version: %s"), firmware.c_str());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
RESP_TYPE_V3 CDVRPTRV3Controller::getResponse(unsigned char *buffer, unsigned int& length)
|
||||
{
|
||||
// Get the start of the frame or nothing at all
|
||||
int ret = readModem(buffer + 0U, 5U);
|
||||
if (ret < 0) {
|
||||
wxLogError(wxT("Error when reading from the DV-RPTR"));
|
||||
return RT3_ERROR;
|
||||
}
|
||||
|
||||
if (ret == 0)
|
||||
return RT3_TIMEOUT;
|
||||
|
||||
while (::memcmp(buffer + 0U, "HEAD", 4U) != 0) {
|
||||
buffer[0U] = buffer[1U];
|
||||
buffer[1U] = buffer[2U];
|
||||
buffer[2U] = buffer[3U];
|
||||
buffer[3U] = buffer[4U];
|
||||
|
||||
ret = readModem(buffer + 4U, 1U);
|
||||
if (ret < 0) {
|
||||
wxLogError(wxT("Error when reading from the DV-RPTR"));
|
||||
return RT3_ERROR;
|
||||
}
|
||||
|
||||
if (ret == 0)
|
||||
return RT3_TIMEOUT;
|
||||
}
|
||||
|
||||
switch (buffer[4U]) {
|
||||
case 'X':
|
||||
length = 105U;
|
||||
break;
|
||||
case 'Y':
|
||||
length = 10U;
|
||||
break;
|
||||
case 'Z':
|
||||
length = 20U;
|
||||
break;
|
||||
default:
|
||||
wxLogError(wxT("DV-RPTR frame type is incorrect - 0x%02X"), buffer[4U]);
|
||||
return RT3_UNKNOWN;
|
||||
}
|
||||
|
||||
unsigned int offset = 5U;
|
||||
|
||||
while (offset < length) {
|
||||
ret = readModem(buffer + offset, length - offset);
|
||||
if (ret < 0) {
|
||||
wxLogError(wxT("Error when reading from the DV-RPTR"));
|
||||
return RT3_ERROR;
|
||||
}
|
||||
|
||||
if (ret > 0)
|
||||
offset += ret;
|
||||
|
||||
if (ret == 0)
|
||||
Sleep(5UL);
|
||||
}
|
||||
|
||||
// CUtils::dump(wxT("Received"), buffer, length);
|
||||
|
||||
if (::memcmp(buffer + 0U, "HEADZ", 5U) == 0) {
|
||||
return RT3_DATA;
|
||||
} else if (::memcmp(buffer + 5U, "0001", 4U) == 0) {
|
||||
if (buffer[104U] == 0x01U)
|
||||
return RT3_HEADER;
|
||||
} else if (::memcmp(buffer + 5U, "9900", 4U) == 0) {
|
||||
return RT3_QUERY;
|
||||
} else if (::memcmp(buffer + 5U, "9001", 4U) == 0) {
|
||||
return RT3_CONFIG;
|
||||
} else if (::memcmp(buffer + 5U, "9011", 4U) == 0) {
|
||||
return RT3_SPACE;
|
||||
} else if (::memcmp(buffer + 5U, "9021", 4U) == 0) {
|
||||
return RT3_TIMEOUT;
|
||||
}
|
||||
|
||||
return RT3_UNKNOWN;
|
||||
}
|
||||
|
||||
wxString CDVRPTRV3Controller::getPath() const
|
||||
{
|
||||
return m_usbPath;
|
||||
}
|
||||
|
||||
bool CDVRPTRV3Controller::findPort()
|
||||
{
|
||||
if (m_connection != CT_USB)
|
||||
return true;
|
||||
|
||||
if (m_usbPath.IsEmpty())
|
||||
return false;
|
||||
|
||||
#if defined(__WINDOWS__)
|
||||
#else
|
||||
wxDir dir;
|
||||
bool ret1 = dir.Open(wxT("/sys/class/tty"));
|
||||
if (!ret1) {
|
||||
wxLogError(wxT("Cannot open directory /sys/class/tty"));
|
||||
return false;
|
||||
}
|
||||
|
||||
wxString fileName;
|
||||
ret1 = dir.GetFirst(&fileName, wxT("ttyACM*"));
|
||||
while (ret1) {
|
||||
wxString path;
|
||||
path.Printf(wxT("/sys/class/tty/%s"), fileName.c_str());
|
||||
|
||||
char cpath[255U];
|
||||
::memset(cpath, 0x00U, 255U);
|
||||
|
||||
for (unsigned int i = 0U; i < path.Len(); i++)
|
||||
cpath[i] = path.GetChar(i);
|
||||
|
||||
char symlink[255U];
|
||||
int ret2 = ::readlink(cpath, symlink, 255U);
|
||||
if (ret2 < 0) {
|
||||
::strcat(cpath, "/device");
|
||||
ret2 = ::readlink(cpath, symlink, 255U);
|
||||
if (ret2 < 0) {
|
||||
wxLogError(wxT("Error from readlink()"));
|
||||
return false;
|
||||
}
|
||||
|
||||
path = wxString(symlink, wxConvLocal, ret2);
|
||||
} else {
|
||||
// Get all but the last section
|
||||
wxString fullPath = wxString(symlink, wxConvLocal, ret2);
|
||||
path = fullPath.BeforeLast(wxT('/'));
|
||||
}
|
||||
|
||||
if (path.IsSameAs(m_usbPath)) {
|
||||
m_usbPort.Printf(wxT("/dev/%s"), fileName.c_str());
|
||||
|
||||
wxLogMessage(wxT("Found modem port of %s based on the path"), m_usbPort.c_str());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ret1 = dir.GetNext(&fileName);
|
||||
}
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CDVRPTRV3Controller::findPath()
|
||||
{
|
||||
if (m_connection != CT_USB)
|
||||
return true;
|
||||
|
||||
#if defined(__WINDOWS__)
|
||||
#ifdef notdef
|
||||
GUID guids[5U];
|
||||
|
||||
DWORD count;
|
||||
BOOL res = ::SetupDiClassGuidsFromName(L"Multifunction", guids, 5U, &count);
|
||||
if (!res) {
|
||||
wxLogError(wxT("Error from SetupDiClassGuidsFromName: err=%u"), ::GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
for (DWORD i = 0U; i < count; i++) {
|
||||
HDEVINFO devInfo = ::SetupDiGetClassDevs(&guids[i], NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
|
||||
if (devInfo == INVALID_HANDLE_VALUE) {
|
||||
wxLogError(wxT("Error from SetupDiGetClassDevs: err=%u"), ::GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
SP_DEVICE_INTERFACE_DATA devInfoData;
|
||||
devInfoData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
|
||||
|
||||
for (DWORD index = 0U; ::SetupDiEnumDeviceInterfaces(devInfo, NULL, &guids[i], index, &devInfoData); index++) {
|
||||
// Find the required length of the device structure
|
||||
DWORD length;
|
||||
::SetupDiGetDeviceInterfaceDetail(devInfo, &devInfoData, NULL, 0U, &length, NULL);
|
||||
|
||||
PSP_DEVICE_INTERFACE_DETAIL_DATA detailData = PSP_DEVICE_INTERFACE_DETAIL_DATA(::malloc(length));
|
||||
detailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
|
||||
|
||||
// Get the detailed data into the newly allocated device structure
|
||||
DWORD required;
|
||||
res = ::SetupDiGetDeviceInterfaceDetail(devInfo, &devInfoData, detailData, length, &required, NULL);
|
||||
if (!res) {
|
||||
wxLogError(wxT("Error from SetupDiGetDeviceInterfaceDetail: err=%u"), ::GetLastError());
|
||||
::SetupDiDestroyDeviceInfoList(devInfo);
|
||||
::free(detailData);
|
||||
return false;
|
||||
}
|
||||
|
||||
::free(detailData);
|
||||
}
|
||||
|
||||
::SetupDiDestroyDeviceInfoList(devInfo);
|
||||
}
|
||||
|
||||
return false;
|
||||
#endif
|
||||
#else
|
||||
wxString path;
|
||||
path.Printf(wxT("/sys/class/tty/%s"), m_usbPort.Mid(5U).c_str());
|
||||
|
||||
char cpath[255U];
|
||||
::memset(cpath, 0x00U, 255U);
|
||||
|
||||
for (unsigned int i = 0U; i < path.Len(); i++)
|
||||
cpath[i] = path.GetChar(i);
|
||||
|
||||
char symlink[255U];
|
||||
int ret = ::readlink(cpath, symlink, 255U);
|
||||
if (ret < 0) {
|
||||
::strcat(cpath, "/device");
|
||||
ret = ::readlink(cpath, symlink, 255U);
|
||||
if (ret < 0) {
|
||||
wxLogError(wxT("Error from readlink()"));
|
||||
return false;
|
||||
}
|
||||
|
||||
path = wxString(symlink, wxConvLocal, ret);
|
||||
} else {
|
||||
wxString fullPath = wxString(symlink, wxConvLocal, ret);
|
||||
path = fullPath.BeforeLast(wxT('/'));
|
||||
}
|
||||
|
||||
if (m_usbPath.IsEmpty())
|
||||
wxLogMessage(wxT("Found modem path of %s"), path.c_str());
|
||||
|
||||
m_usbPath = path;
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDVRPTRV3Controller::findModem()
|
||||
{
|
||||
closeModem();
|
||||
|
||||
// Tell the repeater that the signal has gone away
|
||||
if (m_rx) {
|
||||
wxMutexLocker locker(m_mutex);
|
||||
|
||||
unsigned char data[2U];
|
||||
data[0U] = DSMTT_EOT;
|
||||
data[1U] = 0U;
|
||||
m_rxData.addData(data, 2U);
|
||||
|
||||
m_rx = false;
|
||||
}
|
||||
|
||||
unsigned int count = 0U;
|
||||
|
||||
// Purge the transmit buffer every 500ms to avoid overflow, but only try and reopen the modem every 2s
|
||||
while (!m_stopped) {
|
||||
count++;
|
||||
if (count >= 4U) {
|
||||
wxLogMessage(wxT("Trying to reopen the modem"));
|
||||
|
||||
bool ret = findPort();
|
||||
if (ret) {
|
||||
ret = openModem();
|
||||
if (ret)
|
||||
return true;
|
||||
}
|
||||
|
||||
count = 0U;
|
||||
}
|
||||
|
||||
Sleep(500UL);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CDVRPTRV3Controller::openModem()
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
switch (m_connection) {
|
||||
case CT_USB:
|
||||
ret = m_usb->open();
|
||||
break;
|
||||
case CT_NETWORK:
|
||||
ret = m_network->open();
|
||||
break;
|
||||
default:
|
||||
wxLogError(wxT("Invalid connection type: %d"), int(m_connection));
|
||||
break;
|
||||
}
|
||||
|
||||
if (!ret)
|
||||
return false;
|
||||
|
||||
ret = readSerial();
|
||||
if (!ret) {
|
||||
closeModem();
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = setConfig();
|
||||
if (!ret) {
|
||||
closeModem();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int CDVRPTRV3Controller::readModem(unsigned char* buffer, unsigned int length)
|
||||
{
|
||||
switch (m_connection) {
|
||||
case CT_USB:
|
||||
return m_usb->read(buffer, length);
|
||||
case CT_NETWORK:
|
||||
return m_network->read(buffer, length, 0U);
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
bool CDVRPTRV3Controller::writeModem(const unsigned char* buffer, unsigned int length)
|
||||
{
|
||||
switch (m_connection) {
|
||||
case CT_USB:
|
||||
return m_usb->write(buffer, length) == int(length);
|
||||
case CT_NETWORK:
|
||||
return m_network->write(buffer, length);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void CDVRPTRV3Controller::closeModem()
|
||||
{
|
||||
switch (m_connection) {
|
||||
case CT_USB:
|
||||
return m_usb->close();
|
||||
case CT_NETWORK:
|
||||
return m_network->close();
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2014 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef DVRPTRV3Controller_H
|
||||
#define DVRPTRV3Controller_H
|
||||
|
||||
#include "SerialDataController.h"
|
||||
#include "TCPReaderWriter.h"
|
||||
#include "DStarDefines.h"
|
||||
#include "RingBuffer.h"
|
||||
#include "Modem.h"
|
||||
#include "Utils.h"
|
||||
|
||||
#include <wx/wx.h>
|
||||
|
||||
enum RESP_TYPE_V3 {
|
||||
RT3_TIMEOUT,
|
||||
RT3_ERROR,
|
||||
RT3_UNKNOWN,
|
||||
RT3_SPACE,
|
||||
RT3_QUERY,
|
||||
RT3_CONFIG,
|
||||
RT3_HEADER,
|
||||
RT3_DATA
|
||||
};
|
||||
|
||||
class CDVRPTRV3Controller : public CModem {
|
||||
public:
|
||||
CDVRPTRV3Controller(const wxString& port, const wxString& path, bool txInvert, unsigned int modLevel, bool duplex, const wxString& callsign, unsigned int txDelay);
|
||||
CDVRPTRV3Controller(const wxString& address, unsigned int port, bool txInvert, unsigned int modLevel, bool duplex, const wxString& callsign, unsigned int txDelay);
|
||||
virtual ~CDVRPTRV3Controller();
|
||||
|
||||
virtual void* Entry();
|
||||
|
||||
virtual bool start();
|
||||
|
||||
virtual unsigned int getSpace();
|
||||
virtual bool isTXReady();
|
||||
|
||||
virtual bool writeHeader(const CHeaderData& header);
|
||||
virtual bool writeData(const unsigned char* data, unsigned int length, bool end);
|
||||
|
||||
virtual wxString getPath() const;
|
||||
|
||||
private:
|
||||
CONNECTION_TYPE m_connection;
|
||||
wxString m_usbPort;
|
||||
wxString m_usbPath;
|
||||
wxString m_address;
|
||||
unsigned int m_port;
|
||||
bool m_txInvert;
|
||||
unsigned int m_modLevel;
|
||||
bool m_duplex;
|
||||
wxString m_callsign;
|
||||
unsigned int m_txDelay;
|
||||
CSerialDataController* m_usb;
|
||||
CTCPReaderWriter* m_network;
|
||||
unsigned char* m_buffer;
|
||||
CRingBuffer<unsigned char> m_txData;
|
||||
bool m_rx;
|
||||
|
||||
bool readSerial();
|
||||
bool setConfig();
|
||||
bool readSpace();
|
||||
|
||||
RESP_TYPE_V3 getResponse(unsigned char* buffer, unsigned int& length);
|
||||
|
||||
bool findPort();
|
||||
bool findPath();
|
||||
|
||||
bool findModem();
|
||||
bool openModem();
|
||||
|
||||
int readModem(unsigned char* buffer, unsigned int length);
|
||||
bool writeModem(const unsigned char* buffer, unsigned int length);
|
||||
void closeModem();
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,187 @@
|
||||
/*
|
||||
* Copyright (C) 2009,2013 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "DVTOOLFileReader.h"
|
||||
#include "DStarDefines.h"
|
||||
#include "Utils.h"
|
||||
|
||||
#include <wx/wx.h>
|
||||
|
||||
static const char DVTOOL_SIGNATURE[] = "DVTOOL";
|
||||
static const unsigned int DVTOOL_SIGNATURE_LENGTH = 6U;
|
||||
|
||||
static const char DSVT_SIGNATURE[] = "DSVT";
|
||||
static const unsigned int DSVT_SIGNATURE_LENGTH = 4U;
|
||||
|
||||
static const unsigned int FIXED_DATA_LENGTH = 9U;
|
||||
|
||||
static const unsigned char HEADER_FLAG = 0x10;
|
||||
static const unsigned char DATA_FLAG = 0x20;
|
||||
|
||||
static const unsigned char HEADER_MASK = 0x80;
|
||||
static const unsigned char TRAILER_MASK = 0x40;
|
||||
|
||||
const unsigned int BUFFER_LENGTH = 255U;
|
||||
|
||||
|
||||
CDVTOOLFileReader::CDVTOOLFileReader() :
|
||||
m_fileName(),
|
||||
m_file(),
|
||||
m_records(0U),
|
||||
m_type(DVTFR_NONE),
|
||||
m_buffer(NULL),
|
||||
m_length(0U),
|
||||
m_end(false)
|
||||
{
|
||||
m_buffer = new unsigned char[BUFFER_LENGTH];
|
||||
}
|
||||
|
||||
CDVTOOLFileReader::~CDVTOOLFileReader()
|
||||
{
|
||||
delete[] m_buffer;
|
||||
}
|
||||
|
||||
wxString CDVTOOLFileReader::getFileName() const
|
||||
{
|
||||
return m_fileName;
|
||||
}
|
||||
|
||||
unsigned int CDVTOOLFileReader::getRecords() const
|
||||
{
|
||||
return m_records;
|
||||
}
|
||||
|
||||
bool CDVTOOLFileReader::open(const wxString& fileName)
|
||||
{
|
||||
m_fileName = fileName;
|
||||
|
||||
bool res = m_file.Open(fileName, wxT("rb"));
|
||||
if (!res)
|
||||
return false;
|
||||
|
||||
unsigned char buffer[DVTOOL_SIGNATURE_LENGTH];
|
||||
size_t n = m_file.Read(buffer, DVTOOL_SIGNATURE_LENGTH);
|
||||
if (n != DVTOOL_SIGNATURE_LENGTH) {
|
||||
m_file.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (::memcmp(buffer, DVTOOL_SIGNATURE, DVTOOL_SIGNATURE_LENGTH) != 0) {
|
||||
m_file.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
wxUint32 uint32;
|
||||
n = m_file.Read(&uint32, sizeof(wxUint32));
|
||||
if (n != sizeof(wxUint32)) {
|
||||
m_file.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
m_records = wxUINT32_SWAP_ON_LE(uint32);
|
||||
m_end = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
DVTFR_TYPE CDVTOOLFileReader::read()
|
||||
{
|
||||
wxUint16 uint16;
|
||||
size_t n = m_file.Read(&uint16, sizeof(wxUint16));
|
||||
if (n != sizeof(wxUint16))
|
||||
return DVTFR_NONE;
|
||||
|
||||
m_length = wxUINT16_SWAP_ON_BE(uint16) - 15U;
|
||||
|
||||
unsigned char bytes[FIXED_DATA_LENGTH];
|
||||
n = m_file.Read(bytes, DSVT_SIGNATURE_LENGTH);
|
||||
if (n != DSVT_SIGNATURE_LENGTH)
|
||||
return DVTFR_NONE;
|
||||
|
||||
if (::memcmp(bytes, DSVT_SIGNATURE, DSVT_SIGNATURE_LENGTH) != 0)
|
||||
return DVTFR_NONE;
|
||||
|
||||
char flag;
|
||||
n = m_file.Read(&flag, 1U);
|
||||
if (n != 1U)
|
||||
return DVTFR_NONE;
|
||||
|
||||
m_type = (flag == HEADER_FLAG) ? DVTFR_HEADER : DVTFR_DATA;
|
||||
|
||||
n = m_file.Read(bytes, FIXED_DATA_LENGTH);
|
||||
if (n != FIXED_DATA_LENGTH)
|
||||
return DVTFR_NONE;
|
||||
|
||||
n = m_file.Read(&flag, 1U);
|
||||
if (n != 1U)
|
||||
return DVTFR_NONE;
|
||||
|
||||
if (m_type == DVTFR_DATA) {
|
||||
if ((flag & TRAILER_MASK) == TRAILER_MASK)
|
||||
m_end = true;
|
||||
}
|
||||
|
||||
n = m_file.Read(m_buffer, m_length);
|
||||
if (n != m_length)
|
||||
return DVTFR_NONE;
|
||||
|
||||
return m_type;
|
||||
}
|
||||
|
||||
CHeaderData* CDVTOOLFileReader::readHeader()
|
||||
{
|
||||
if (m_type != DVTFR_HEADER)
|
||||
return NULL;
|
||||
|
||||
if (m_buffer[39U] == 0xFFU && m_buffer[40U] == 0xFFU)
|
||||
return new CHeaderData(m_buffer, RADIO_HEADER_LENGTH_BYTES, false);
|
||||
|
||||
// Header checksum testing is enabled
|
||||
CHeaderData* header = new CHeaderData(m_buffer, RADIO_HEADER_LENGTH_BYTES, true);
|
||||
|
||||
if (!header->isValid()) {
|
||||
CUtils::dump(wxT("Header checksum failure"), m_buffer, RADIO_HEADER_LENGTH_BYTES);
|
||||
delete header;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
unsigned int CDVTOOLFileReader::readData(unsigned char* buffer, unsigned int length, bool& end)
|
||||
{
|
||||
wxASSERT(buffer != NULL);
|
||||
wxASSERT(length > 0U);
|
||||
|
||||
if (m_type != DVTFR_DATA)
|
||||
return 0U;
|
||||
|
||||
if (length > m_length)
|
||||
length = m_length;
|
||||
|
||||
end = m_end;
|
||||
|
||||
::memcpy(buffer, m_buffer, length);
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
void CDVTOOLFileReader::close()
|
||||
{
|
||||
m_file.Close();
|
||||
}
|
||||
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) 2009 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef DVTOOLFileReader_H
|
||||
#define DVTOOLFileReader_H
|
||||
|
||||
#include "HeaderData.h"
|
||||
|
||||
enum DVTFR_TYPE {
|
||||
DVTFR_NONE,
|
||||
DVTFR_HEADER,
|
||||
DVTFR_DATA
|
||||
};
|
||||
|
||||
#include <wx/wx.h>
|
||||
#include <wx/ffile.h>
|
||||
|
||||
class CDVTOOLFileReader {
|
||||
public:
|
||||
CDVTOOLFileReader();
|
||||
~CDVTOOLFileReader();
|
||||
|
||||
wxString getFileName() const;
|
||||
unsigned int getRecords() const;
|
||||
|
||||
bool open(const wxString& fileName);
|
||||
DVTFR_TYPE read();
|
||||
CHeaderData* readHeader();
|
||||
unsigned int readData(unsigned char* buffer, unsigned int length, bool& end);
|
||||
void close();
|
||||
|
||||
private:
|
||||
wxString m_fileName;
|
||||
wxFFile m_file;
|
||||
wxUint32 m_records;
|
||||
DVTFR_TYPE m_type;
|
||||
unsigned char* m_buffer;
|
||||
unsigned int m_length;
|
||||
bool m_end;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,346 @@
|
||||
/*
|
||||
* Copyright (C) 2009,2011,2013 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "CCITTChecksumReverse.h"
|
||||
#include "DVTOOLFileWriter.h"
|
||||
#include "DStarDefines.h"
|
||||
|
||||
#include <wx/wx.h>
|
||||
#include <wx/filename.h>
|
||||
|
||||
static const char DVTOOL_SIGNATURE[] = "DVTOOL";
|
||||
static unsigned int DVTOOL_SIGNATURE_LENGTH = 6U;
|
||||
|
||||
static const char DSVT_SIGNATURE[] = "DSVT";
|
||||
static unsigned int DSVT_SIGNATURE_LENGTH = 4U;
|
||||
|
||||
static const unsigned char HEADER_FLAG = 0x10;
|
||||
static const unsigned char DATA_FLAG = 0x20;
|
||||
|
||||
static const unsigned char FIXED_DATA[] = {0x00, 0x81, 0x00, 0x20, 0x00, 0x01, 0x02, 0xC0, 0xDE};
|
||||
static unsigned int FIXED_DATA_LENGTH = 9U;
|
||||
|
||||
static const unsigned char TRAILER_DATA[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
static unsigned int TRAILER_DATA_LENGTH = 12U;
|
||||
|
||||
static const unsigned char HEADER_MASK = 0x80;
|
||||
static const unsigned char TRAILER_MASK = 0x40;
|
||||
|
||||
wxString CDVTOOLFileWriter::m_dirName = wxEmptyString;
|
||||
|
||||
CDVTOOLFileWriter::CDVTOOLFileWriter() :
|
||||
m_fileName(),
|
||||
m_file(),
|
||||
m_count(0U),
|
||||
m_sequence(0U),
|
||||
m_offset(0)
|
||||
{
|
||||
}
|
||||
|
||||
CDVTOOLFileWriter::~CDVTOOLFileWriter()
|
||||
{
|
||||
}
|
||||
|
||||
void CDVTOOLFileWriter::setDirectory(const wxString& dirName)
|
||||
{
|
||||
m_dirName = dirName;
|
||||
}
|
||||
|
||||
wxString CDVTOOLFileWriter::getFileName() const
|
||||
{
|
||||
return m_fileName;
|
||||
}
|
||||
|
||||
bool CDVTOOLFileWriter::open(const wxString& filename, const CHeaderData& header)
|
||||
{
|
||||
if (m_file.IsOpened())
|
||||
close();
|
||||
|
||||
wxString name = filename;
|
||||
#if !defined(__WINDOWS__)
|
||||
name.Replace(wxT(" "), wxT("_"));
|
||||
#endif
|
||||
|
||||
wxFileName fileName(m_dirName, name, wxT("dvtool"));
|
||||
m_fileName = fileName.GetFullPath();
|
||||
|
||||
bool res = m_file.Open(m_fileName, wxT("wb"));
|
||||
if (!res)
|
||||
return false;
|
||||
|
||||
size_t n = m_file.Write(DVTOOL_SIGNATURE, DVTOOL_SIGNATURE_LENGTH);
|
||||
if (n != DVTOOL_SIGNATURE_LENGTH) {
|
||||
m_file.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
m_offset = m_file.Tell();
|
||||
|
||||
wxUint32 dummy = 0U;
|
||||
n = m_file.Write(&dummy, sizeof(wxUint32));
|
||||
if (n != sizeof(wxUint32)) {
|
||||
m_file.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
m_sequence = 0U;
|
||||
m_count = 0U;
|
||||
|
||||
res = writeHeader(header);
|
||||
if (!res) {
|
||||
m_file.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDVTOOLFileWriter::open(const CHeaderData& header)
|
||||
{
|
||||
if (m_file.IsOpened())
|
||||
close();
|
||||
|
||||
wxDateTime time;
|
||||
time.SetToCurrent();
|
||||
|
||||
wxString name = time.Format(wxT("%Y%m%d-%H%M%S-"));
|
||||
|
||||
name.Append(header.getRptCall1());
|
||||
name.Append(header.getRptCall2());
|
||||
name.Append(header.getYourCall());
|
||||
name.Append(header.getMyCall1());
|
||||
name.Append(header.getMyCall2());
|
||||
|
||||
#if !defined(__WINDOWS__)
|
||||
name.Replace(wxT(" "), wxT("_"));
|
||||
#endif
|
||||
name.Replace(wxT("/"), wxT("-"));
|
||||
|
||||
wxFileName fileName(m_dirName, name, wxT("dvtool"));
|
||||
m_fileName = fileName.GetFullPath();
|
||||
|
||||
bool res = m_file.Open(m_fileName, wxT("wb"));
|
||||
if (!res)
|
||||
return false;
|
||||
|
||||
size_t n = m_file.Write(DVTOOL_SIGNATURE, DVTOOL_SIGNATURE_LENGTH);
|
||||
if (n != DVTOOL_SIGNATURE_LENGTH) {
|
||||
m_file.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
m_offset = m_file.Tell();
|
||||
|
||||
wxUint32 dummy = 0U;
|
||||
n = m_file.Write(&dummy, sizeof(wxUint32));
|
||||
if (n != sizeof(wxUint32)) {
|
||||
m_file.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
m_sequence = 0U;
|
||||
m_count = 0U;
|
||||
|
||||
res = writeHeader(header);
|
||||
if (!res) {
|
||||
m_file.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDVTOOLFileWriter::write(const unsigned char* buffer, unsigned int length)
|
||||
{
|
||||
wxASSERT(buffer != 0);
|
||||
wxASSERT(length > 0U);
|
||||
|
||||
wxUint16 len = wxUINT16_SWAP_ON_BE(length + 15U);
|
||||
size_t n = m_file.Write(&len, sizeof(wxUint16));
|
||||
if (n != sizeof(wxUint16)) {
|
||||
m_file.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
n = m_file.Write(DSVT_SIGNATURE, DSVT_SIGNATURE_LENGTH);
|
||||
if (n != DSVT_SIGNATURE_LENGTH) {
|
||||
m_file.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
char byte = DATA_FLAG;
|
||||
n = m_file.Write(&byte, 1U);
|
||||
if (n != 1U) {
|
||||
m_file.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
n = m_file.Write(FIXED_DATA, FIXED_DATA_LENGTH);
|
||||
if (n != FIXED_DATA_LENGTH) {
|
||||
m_file.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
byte = m_sequence;
|
||||
n = m_file.Write(&byte, 1U);
|
||||
if (n != 1U) {
|
||||
m_file.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
n = m_file.Write(buffer, length);
|
||||
if (n != length) {
|
||||
m_file.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
m_count++;
|
||||
m_sequence++;
|
||||
if (m_sequence >= 0x15U)
|
||||
m_sequence = 0U;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CDVTOOLFileWriter::close()
|
||||
{
|
||||
writeTrailer();
|
||||
|
||||
m_file.Seek(m_offset);
|
||||
|
||||
wxUint32 count = wxUINT32_SWAP_ON_LE(m_count);
|
||||
m_file.Write(&count, sizeof(wxUint32));
|
||||
|
||||
m_file.Close();
|
||||
}
|
||||
|
||||
bool CDVTOOLFileWriter::writeHeader(const CHeaderData& header)
|
||||
{
|
||||
unsigned char buffer[RADIO_HEADER_LENGTH_BYTES];
|
||||
|
||||
buffer[0] = header.getFlag1();
|
||||
buffer[1] = header.getFlag2();
|
||||
buffer[2] = header.getFlag3();
|
||||
|
||||
for (unsigned int i = 0U; i < LONG_CALLSIGN_LENGTH; i++)
|
||||
buffer[3 + i] = header.getRptCall1().GetChar(i);
|
||||
|
||||
for (unsigned int i = 0U; i < LONG_CALLSIGN_LENGTH; i++)
|
||||
buffer[11 + i] = header.getRptCall2().GetChar(i);
|
||||
|
||||
for (unsigned int i = 0U; i < LONG_CALLSIGN_LENGTH; i++)
|
||||
buffer[19 + i] = header.getYourCall().GetChar(i);
|
||||
|
||||
for (unsigned int i = 0U; i < LONG_CALLSIGN_LENGTH; i++)
|
||||
buffer[27 + i] = header.getMyCall1().GetChar(i);
|
||||
|
||||
for (unsigned int i = 0U; i < SHORT_CALLSIGN_LENGTH; i++)
|
||||
buffer[35 + i] = header.getMyCall2().GetChar(i);
|
||||
|
||||
// Get the checksum for the header
|
||||
CCCITTChecksumReverse csum;
|
||||
csum.update(buffer, RADIO_HEADER_LENGTH_BYTES - 2U);
|
||||
csum.result(buffer + 39U);
|
||||
|
||||
wxUint16 len = wxUINT16_SWAP_ON_BE(RADIO_HEADER_LENGTH_BYTES + 15U);
|
||||
size_t n = m_file.Write(&len, sizeof(wxUint16));
|
||||
if (n != sizeof(wxUint16)) {
|
||||
m_file.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
n = m_file.Write(DSVT_SIGNATURE, DSVT_SIGNATURE_LENGTH);
|
||||
if (n != DSVT_SIGNATURE_LENGTH) {
|
||||
m_file.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
char byte = HEADER_FLAG;
|
||||
n = m_file.Write(&byte, 1U);
|
||||
if (n != 1U) {
|
||||
m_file.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
n = m_file.Write(FIXED_DATA, FIXED_DATA_LENGTH);
|
||||
if (n != FIXED_DATA_LENGTH) {
|
||||
m_file.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
byte = HEADER_MASK;
|
||||
n = m_file.Write(&byte, 1U);
|
||||
if (n != 1U) {
|
||||
m_file.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
n = m_file.Write(buffer, RADIO_HEADER_LENGTH_BYTES);
|
||||
if (n != RADIO_HEADER_LENGTH_BYTES) {
|
||||
m_file.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
m_count++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDVTOOLFileWriter::writeTrailer()
|
||||
{
|
||||
wxUint16 len = wxUINT16_SWAP_ON_BE(27U);
|
||||
size_t n = m_file.Write(&len, sizeof(wxUint16));
|
||||
if (n != sizeof(wxUint16)) {
|
||||
m_file.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
n = m_file.Write(DSVT_SIGNATURE, DSVT_SIGNATURE_LENGTH);
|
||||
if (n != DSVT_SIGNATURE_LENGTH) {
|
||||
m_file.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
char byte = DATA_FLAG;
|
||||
n = m_file.Write(&byte, 1U);
|
||||
if (n != 1U) {
|
||||
m_file.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
n = m_file.Write(FIXED_DATA, FIXED_DATA_LENGTH);
|
||||
if (n != FIXED_DATA_LENGTH) {
|
||||
m_file.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
byte = TRAILER_MASK | m_sequence;
|
||||
n = m_file.Write(&byte, 1U);
|
||||
if (n != 1U) {
|
||||
m_file.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
n = m_file.Write(TRAILER_DATA, TRAILER_DATA_LENGTH);
|
||||
if (n != TRAILER_DATA_LENGTH) {
|
||||
m_file.Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) 2009,2013 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef DVTOOLFileWriter_H
|
||||
#define DVTOOLFileWriter_H
|
||||
|
||||
#include "HeaderData.h"
|
||||
|
||||
#include <wx/wx.h>
|
||||
#include <wx/ffile.h>
|
||||
|
||||
class CDVTOOLFileWriter {
|
||||
public:
|
||||
CDVTOOLFileWriter();
|
||||
~CDVTOOLFileWriter();
|
||||
|
||||
static void setDirectory(const wxString& dirName);
|
||||
|
||||
wxString getFileName() const;
|
||||
|
||||
bool open(const CHeaderData& header);
|
||||
bool open(const wxString& filename, const CHeaderData& header);
|
||||
bool write(const unsigned char* buffer, unsigned int length);
|
||||
void close();
|
||||
|
||||
private:
|
||||
static wxString m_dirName;
|
||||
|
||||
wxString m_fileName;
|
||||
wxFFile m_file;
|
||||
wxUint32 m_count;
|
||||
unsigned int m_sequence;
|
||||
wxFileOffset m_offset;
|
||||
|
||||
bool writeHeader(const CHeaderData& header);
|
||||
bool writeTrailer();
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2009,2013 by Jonathan Naylor, G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "DummyController.h"
|
||||
|
||||
#include <wx/wx.h>
|
||||
|
||||
CDummyController::CDummyController()
|
||||
{
|
||||
}
|
||||
|
||||
CDummyController::~CDummyController()
|
||||
{
|
||||
}
|
||||
|
||||
bool CDummyController::open()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void CDummyController::getDigitalInputs(bool& inp1, bool& inp2, bool& inp3, bool& inp4, bool& inp5)
|
||||
{
|
||||
inp1 = false;
|
||||
inp2 = false;
|
||||
inp3 = false;
|
||||
inp4 = false;
|
||||
inp5 = false;
|
||||
}
|
||||
|
||||
void CDummyController::setDigitalOutputs(bool, bool, bool, bool, bool, bool, bool, bool)
|
||||
{
|
||||
}
|
||||
|
||||
void CDummyController::close()
|
||||
{
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (C) 2009 by Jonathan Naylor, G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef DummyController_H
|
||||
#define DummyController_H
|
||||
|
||||
#include "HardwareController.h"
|
||||
|
||||
class CDummyController : public IHardwareController {
|
||||
public:
|
||||
CDummyController();
|
||||
virtual ~CDummyController();
|
||||
|
||||
virtual bool open();
|
||||
|
||||
virtual void getDigitalInputs(bool& inp1, bool& inp2, bool& inp3, bool& inp4, bool& inp5);
|
||||
|
||||
virtual void setDigitalOutputs(bool outp1, bool outp2, bool outp3, bool outp4, bool outp5, bool outp6, bool outp7, bool outp8);
|
||||
|
||||
virtual void close();
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,135 @@
|
||||
/*
|
||||
* Copyright (C) 2009,2010,2014 by Jonathan Naylor, G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "ExternalController.h"
|
||||
|
||||
#include "DStarDefines.h"
|
||||
|
||||
CExternalController::CExternalController(IHardwareController* controller, bool pttInvert) :
|
||||
wxThread(wxTHREAD_JOINABLE),
|
||||
m_controller(controller),
|
||||
m_pttInvert(pttInvert),
|
||||
m_disable(false),
|
||||
m_heartbeat(false),
|
||||
m_active(false),
|
||||
m_radioTX(false),
|
||||
m_out1(false),
|
||||
m_out2(false),
|
||||
m_out3(false),
|
||||
m_out4(false),
|
||||
m_kill(false)
|
||||
{
|
||||
// wxASSERT(controller != NULL);
|
||||
|
||||
if (m_pttInvert)
|
||||
m_radioTX = true;
|
||||
}
|
||||
|
||||
CExternalController::~CExternalController()
|
||||
{
|
||||
delete m_controller;
|
||||
}
|
||||
|
||||
bool CExternalController::open()
|
||||
{
|
||||
bool res = m_controller->open();
|
||||
if (!res)
|
||||
return false;
|
||||
|
||||
Create();
|
||||
Run();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void* CExternalController::Entry()
|
||||
{
|
||||
wxASSERT(m_controller != NULL);
|
||||
|
||||
bool dummy1, dummy2, dummy3, dummy4;
|
||||
|
||||
while (!m_kill) {
|
||||
m_controller->setDigitalOutputs(m_radioTX, false, m_heartbeat, m_active, m_out1, m_out2, m_out3, m_out4);
|
||||
m_controller->getDigitalInputs(dummy1, dummy2, dummy3, dummy4, m_disable);
|
||||
|
||||
Sleep(DSTAR_FRAME_TIME_MS / 2U);
|
||||
}
|
||||
|
||||
if (m_pttInvert)
|
||||
m_controller->setDigitalOutputs(true, false, false, false, false, false, false, false);
|
||||
else
|
||||
m_controller->setDigitalOutputs(false, false, false, false, false, false, false, false);
|
||||
m_controller->getDigitalInputs(dummy1, dummy2, dummy3, dummy4, m_disable);
|
||||
|
||||
Sleep(DSTAR_FRAME_TIME_MS * 3U);
|
||||
|
||||
if (m_pttInvert)
|
||||
m_controller->setDigitalOutputs(true, false, false, false, false, false, false, false);
|
||||
else
|
||||
m_controller->setDigitalOutputs(false, false, false, false, false, false, false, false);
|
||||
m_controller->getDigitalInputs(dummy1, dummy2, dummy3, dummy4, m_disable);
|
||||
|
||||
m_controller->close();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool CExternalController::getDisable() const
|
||||
{
|
||||
return m_disable;
|
||||
}
|
||||
|
||||
void CExternalController::setRadioTransmit(bool value)
|
||||
{
|
||||
if (m_pttInvert)
|
||||
value = !value;
|
||||
|
||||
m_radioTX = value;
|
||||
}
|
||||
|
||||
void CExternalController::setHeartbeat()
|
||||
{
|
||||
m_heartbeat = !m_heartbeat;
|
||||
}
|
||||
|
||||
void CExternalController::setActive(bool value)
|
||||
{
|
||||
m_active = value;
|
||||
}
|
||||
|
||||
void CExternalController::setOutput1(bool value)
|
||||
{
|
||||
m_out1 = value;
|
||||
}
|
||||
|
||||
void CExternalController::setOutput2(bool value)
|
||||
{
|
||||
m_out2 = value;
|
||||
}
|
||||
|
||||
void CExternalController::setOutput3(bool value)
|
||||
{
|
||||
m_out3 = value;
|
||||
}
|
||||
|
||||
void CExternalController::setOutput4(bool value)
|
||||
{
|
||||
m_out4 = value;
|
||||
}
|
||||
|
||||
void CExternalController::close()
|
||||
{
|
||||
m_kill = true;
|
||||
|
||||
Wait();
|
||||
}
|
||||
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (C) 2009,2010,2013,2014 by Jonathan Naylor, G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef ExternalController_H
|
||||
#define ExternalController_H
|
||||
|
||||
#include "HardwareController.h"
|
||||
|
||||
#include <wx/wx.h>
|
||||
|
||||
class CExternalController : public wxThread {
|
||||
public:
|
||||
CExternalController(IHardwareController* controller, bool pttInvert);
|
||||
virtual ~CExternalController();
|
||||
|
||||
virtual bool open();
|
||||
|
||||
virtual bool getDisable() const;
|
||||
|
||||
virtual void setRadioTransmit(bool value);
|
||||
virtual void setHeartbeat();
|
||||
virtual void setActive(bool value);
|
||||
virtual void setOutput1(bool value);
|
||||
virtual void setOutput2(bool value);
|
||||
virtual void setOutput3(bool value);
|
||||
virtual void setOutput4(bool value);
|
||||
|
||||
virtual void close();
|
||||
|
||||
virtual void* Entry();
|
||||
|
||||
private:
|
||||
IHardwareController* m_controller;
|
||||
bool m_pttInvert;
|
||||
bool m_disable;
|
||||
bool m_heartbeat;
|
||||
bool m_active;
|
||||
bool m_radioTX;
|
||||
bool m_out1;
|
||||
bool m_out2;
|
||||
bool m_out3;
|
||||
bool m_out4;
|
||||
bool m_kill;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright (C) 2001, 2002, 2003 by Tomi Manninen, OH2BNS
|
||||
* Copyright (C) 2009 by Jonathan Naylor, G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "FIRFilter.h"
|
||||
|
||||
CFIRFilter::CFIRFilter(const wxFloat32* taps, unsigned int length) :
|
||||
m_taps(NULL),
|
||||
m_length(length),
|
||||
m_buffer(NULL),
|
||||
m_bufLen(20U * m_length),
|
||||
m_pointer(length)
|
||||
{
|
||||
wxASSERT(taps != NULL);
|
||||
wxASSERT(length > 0U);
|
||||
|
||||
m_taps = new wxFloat32[m_length];
|
||||
m_buffer = new wxFloat32[m_bufLen];
|
||||
|
||||
::memcpy(m_taps, taps, m_length * sizeof(wxFloat32));
|
||||
::memset(m_buffer, 0x00, m_bufLen * sizeof(wxFloat32));
|
||||
}
|
||||
|
||||
CFIRFilter::CFIRFilter() :
|
||||
m_taps(NULL),
|
||||
m_length(0U),
|
||||
m_buffer(NULL),
|
||||
m_bufLen(0U),
|
||||
m_pointer(0U)
|
||||
{
|
||||
}
|
||||
|
||||
CFIRFilter::~CFIRFilter()
|
||||
{
|
||||
delete[] m_taps;
|
||||
delete[] m_buffer;
|
||||
}
|
||||
|
||||
void CFIRFilter::setTaps(const wxFloat32* taps, unsigned int length)
|
||||
{
|
||||
wxASSERT(taps != NULL);
|
||||
wxASSERT(length > 0U);
|
||||
|
||||
delete[] m_taps;
|
||||
delete[] m_buffer;
|
||||
|
||||
m_length = length;
|
||||
m_pointer = length;
|
||||
m_bufLen = 20U * m_length;
|
||||
|
||||
m_taps = new wxFloat32[m_length];
|
||||
m_buffer = new wxFloat32[m_bufLen];
|
||||
|
||||
::memcpy(m_taps, taps, m_length * sizeof(wxFloat32));
|
||||
::memset(m_buffer, 0x00, m_bufLen * sizeof(wxFloat32));
|
||||
}
|
||||
|
||||
wxFloat32 CFIRFilter::process(wxFloat32 val)
|
||||
{
|
||||
wxFloat32* ptr = m_buffer + m_pointer++;
|
||||
|
||||
*ptr = val;
|
||||
|
||||
wxFloat32* a = ptr - m_length;
|
||||
wxFloat32* b = m_taps;
|
||||
|
||||
wxFloat32 out = 0.0F;
|
||||
for (unsigned int i = 0U; i < m_length; i++)
|
||||
out += (*a++) * (*b++);
|
||||
|
||||
if (m_pointer == m_bufLen) {
|
||||
::memcpy(m_buffer, m_buffer + m_bufLen - m_length, m_length * sizeof(wxFloat32));
|
||||
m_pointer = m_length;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void CFIRFilter::process(wxFloat32* inOut, unsigned int length)
|
||||
{
|
||||
wxASSERT(inOut != NULL);
|
||||
|
||||
for (unsigned int i = 0U; i < length; i++)
|
||||
inOut[i] = process(inOut[i]);
|
||||
}
|
||||
|
||||
void CFIRFilter::process(const wxFloat32* in, wxFloat32* out, unsigned int length)
|
||||
{
|
||||
wxASSERT(in != NULL);
|
||||
wxASSERT(out != NULL);
|
||||
|
||||
for (unsigned int i = 0U; i < length; i++)
|
||||
out[i] = process(in[i]);
|
||||
}
|
||||
|
||||
void CFIRFilter::reset()
|
||||
{
|
||||
::memset(m_buffer, 0x00, m_bufLen * sizeof(wxFloat32));;
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (C) 2009,2013 by Jonathan Naylor, G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef FIRFilter_H
|
||||
#define FIRFilter_H
|
||||
|
||||
#include <wx/wx.h>
|
||||
|
||||
class CFIRFilter {
|
||||
public:
|
||||
CFIRFilter(const wxFloat32* taps, unsigned int length);
|
||||
CFIRFilter();
|
||||
~CFIRFilter();
|
||||
|
||||
void setTaps(const wxFloat32* taps, unsigned int length);
|
||||
|
||||
wxFloat32 process(wxFloat32 val);
|
||||
void process(wxFloat32* inOut, unsigned int length);
|
||||
void process(const wxFloat32* in, wxFloat32* out, unsigned int length);
|
||||
|
||||
void reset();
|
||||
|
||||
private:
|
||||
wxFloat32* m_taps;
|
||||
unsigned int m_length;
|
||||
wxFloat32* m_buffer;
|
||||
unsigned int m_bufLen;
|
||||
unsigned int m_pointer;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,374 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2014 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "GMSKController.h"
|
||||
#if defined(__WINDOWS__)
|
||||
#include "GMSKModemWinUSB.h"
|
||||
#endif
|
||||
#include "GMSKModemLibUsb.h"
|
||||
#include "Timer.h"
|
||||
|
||||
const unsigned char DVRPTR_HEADER_LENGTH = 5U;
|
||||
|
||||
const unsigned int BUFFER_LENGTH = 200U;
|
||||
|
||||
const unsigned int CYCLE_TIME = 15U;
|
||||
|
||||
CGMSKController::CGMSKController(USB_INTERFACE iface, unsigned int address, bool duplex) :
|
||||
CModem(),
|
||||
m_modem(NULL),
|
||||
m_duplex(duplex),
|
||||
m_buffer(NULL),
|
||||
m_txData(1000U)
|
||||
{
|
||||
wxASSERT(address > 0U);
|
||||
|
||||
m_buffer = new unsigned char[BUFFER_LENGTH];
|
||||
|
||||
#if defined(__WINDOWS__)
|
||||
switch (iface) {
|
||||
case UI_LIBUSB:
|
||||
m_modem = new CGMSKModemLibUsb(address);
|
||||
break;
|
||||
case UI_WINUSB:
|
||||
m_modem = new CGMSKModemWinUSB(address);
|
||||
break;
|
||||
default:
|
||||
wxLogError(wxT("Unknown GMSM modem driver type - %d"), int(iface));
|
||||
break;
|
||||
}
|
||||
#else
|
||||
m_modem = new CGMSKModemLibUsb(address);
|
||||
#endif
|
||||
}
|
||||
|
||||
CGMSKController::~CGMSKController()
|
||||
{
|
||||
delete[] m_buffer;
|
||||
}
|
||||
|
||||
bool CGMSKController::start()
|
||||
{
|
||||
if (m_modem == NULL)
|
||||
return false;
|
||||
|
||||
bool ret = m_modem->open();
|
||||
if (!ret) {
|
||||
delete m_modem;
|
||||
return false;
|
||||
}
|
||||
|
||||
Create();
|
||||
SetPriority(100U);
|
||||
Run();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void* CGMSKController::Entry()
|
||||
{
|
||||
wxLogMessage(wxT("Starting GMSK Modem Controller thread"));
|
||||
|
||||
CTimer hdrTimer(1000U, 0U, 100U);
|
||||
hdrTimer.start();
|
||||
|
||||
CTimer dataTimer(1000U, 0U, 100U);
|
||||
|
||||
bool rx = false;
|
||||
|
||||
DSMT_TYPE writeType = DSMTT_HEADER;
|
||||
unsigned char writeLength = 0U;
|
||||
unsigned char* writeBuffer = new unsigned char[BUFFER_LENGTH];
|
||||
|
||||
unsigned char readLength = 0U;
|
||||
unsigned char* readBuffer = new unsigned char[DV_FRAME_LENGTH_BYTES];
|
||||
|
||||
wxStopWatch stopWatch;
|
||||
|
||||
while (!m_stopped) {
|
||||
stopWatch.Start();
|
||||
|
||||
// Only receive when not transmitting or when in duplex mode
|
||||
if (!m_tx || m_duplex) {
|
||||
if (rx) {
|
||||
unsigned char buffer[GMSK_MODEM_DATA_LENGTH];
|
||||
bool end;
|
||||
int ret = m_modem->readData(buffer, GMSK_MODEM_DATA_LENGTH, end);
|
||||
if (ret >= 0) {
|
||||
// CUtils::dump(wxT("Read Data"), buffer, ret);
|
||||
|
||||
if (end) {
|
||||
wxMutexLocker locker(m_mutex);
|
||||
|
||||
unsigned char data[2U];
|
||||
data[0U] = DSMTT_EOT;
|
||||
data[1U] = 0U;
|
||||
m_rxData.addData(data, 2U);
|
||||
|
||||
hdrTimer.start();
|
||||
readLength = 0U;
|
||||
rx = false;
|
||||
} else {
|
||||
for (int i = 0; i < ret; i++) {
|
||||
readBuffer[readLength] = buffer[i];
|
||||
|
||||
readLength++;
|
||||
if (readLength >= DV_FRAME_LENGTH_BYTES) {
|
||||
wxMutexLocker locker(m_mutex);
|
||||
|
||||
unsigned char data[2U];
|
||||
data[0U] = DSMTT_DATA;
|
||||
data[1U] = DV_FRAME_LENGTH_BYTES;
|
||||
m_rxData.addData(data, 2U);
|
||||
|
||||
m_rxData.addData(readBuffer, DV_FRAME_LENGTH_BYTES);
|
||||
readLength = 0U;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ret = reopenModem();
|
||||
if (!ret)
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// Check for a header every 100ms or so
|
||||
if (hdrTimer.isRunning() && hdrTimer.hasExpired()) {
|
||||
unsigned char buffer[90U];
|
||||
bool ret = m_modem->readHeader(buffer, 90U);
|
||||
if (ret) {
|
||||
// CUtils::dump(wxT("Read Header"), buffer, RADIO_HEADER_LENGTH_BYTES);
|
||||
|
||||
wxMutexLocker locker(m_mutex);
|
||||
|
||||
unsigned char data[2U];
|
||||
data[0U] = DSMTT_HEADER;
|
||||
data[1U] = RADIO_HEADER_LENGTH_BYTES - 2U;
|
||||
m_rxData.addData(data, 2U);
|
||||
|
||||
m_rxData.addData(buffer, RADIO_HEADER_LENGTH_BYTES - 2U);
|
||||
|
||||
hdrTimer.stop();
|
||||
readLength = 0U;
|
||||
rx = true;
|
||||
} else {
|
||||
hdrTimer.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Only transmit when not receiving or when in duplex mode
|
||||
if (!rx || m_duplex) {
|
||||
if (writeLength == 0U && m_txData.hasData()) {
|
||||
wxMutexLocker locker(m_mutex);
|
||||
|
||||
unsigned char type = DSMTT_NONE;
|
||||
m_txData.getData(&type, 1U);
|
||||
writeType = DSMT_TYPE(type);
|
||||
|
||||
m_txData.getData(&writeLength, 1U);
|
||||
m_txData.getData(writeBuffer, writeLength);
|
||||
}
|
||||
|
||||
if (writeLength > 0U) {
|
||||
if (writeType == DSMTT_HEADER) {
|
||||
TRISTATE tx = m_modem->getPTT();
|
||||
|
||||
// Check that the modem isn't still transmitting before sending the new header
|
||||
if (tx == STATE_FALSE) {
|
||||
// CUtils::dump(wxT("Write Header"), writeBuffer, writeLength);
|
||||
m_modem->writeHeader(writeBuffer, writeLength);
|
||||
m_modem->setPTT(true);
|
||||
dataTimer.start();
|
||||
writeLength = 0U;
|
||||
m_tx = true;
|
||||
} else if (tx == STATE_UNKNOWN) {
|
||||
bool ret = reopenModem();
|
||||
if (!ret)
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// Don't start sending data until the header has been gone for 100ms or so
|
||||
if (!dataTimer.isRunning() || dataTimer.hasExpired()) {
|
||||
dataTimer.stop();
|
||||
|
||||
TRISTATE ret = m_modem->hasSpace();
|
||||
|
||||
// Check that there is space in the modem buffer
|
||||
if (ret == STATE_TRUE) {
|
||||
// CUtils::dump(wxT("Write Data"), writeBuffer, writeLength);
|
||||
int ret = m_modem->writeData(writeBuffer, writeLength);
|
||||
if (ret > 0) {
|
||||
writeLength -= ret;
|
||||
|
||||
if (writeType == DSMTT_EOT) {
|
||||
m_modem->setPTT(false);
|
||||
m_tx = false;
|
||||
}
|
||||
} else if (ret < 0) {
|
||||
ret = reopenModem();
|
||||
if (!ret)
|
||||
break;
|
||||
}
|
||||
} else if (ret == STATE_UNKNOWN) {
|
||||
bool ret = reopenModem();
|
||||
if (!ret)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned long ms = stopWatch.Time();
|
||||
|
||||
// Don't sleep when reading from the modem
|
||||
if (!rx) {
|
||||
if (ms < CYCLE_TIME)
|
||||
Sleep(CYCLE_TIME - ms);
|
||||
|
||||
ms = stopWatch.Time();
|
||||
}
|
||||
|
||||
// Catch up with the clock
|
||||
dataTimer.clock(ms);
|
||||
hdrTimer.clock(ms);
|
||||
}
|
||||
|
||||
wxLogMessage(wxT("Stopping GMSK Modem Controller thread"));
|
||||
|
||||
if (m_modem != NULL) {
|
||||
m_modem->close();
|
||||
delete m_modem;
|
||||
}
|
||||
|
||||
delete[] writeBuffer;
|
||||
delete[] readBuffer;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool CGMSKController::writeHeader(const CHeaderData& header)
|
||||
{
|
||||
bool ret = m_txData.hasSpace(RADIO_HEADER_LENGTH_BYTES);
|
||||
if (!ret) {
|
||||
wxLogWarning(wxT("No space to write the header"));
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned char buffer[50U];
|
||||
|
||||
::memset(buffer, ' ', RADIO_HEADER_LENGTH_BYTES - 2U);
|
||||
|
||||
buffer[0U] = header.getFlag1();
|
||||
buffer[1U] = header.getFlag2();
|
||||
buffer[2U] = header.getFlag3();
|
||||
|
||||
wxString rpt2 = header.getRptCall2();
|
||||
for (unsigned int i = 0U; i < rpt2.Len() && i < LONG_CALLSIGN_LENGTH; i++)
|
||||
buffer[i + 3U] = rpt2.GetChar(i);
|
||||
|
||||
wxString rpt1 = header.getRptCall1();
|
||||
for (unsigned int i = 0U; i < rpt1.Len() && i < LONG_CALLSIGN_LENGTH; i++)
|
||||
buffer[i + 11U] = rpt1.GetChar(i);
|
||||
|
||||
wxString your = header.getYourCall();
|
||||
for (unsigned int i = 0U; i < your.Len() && i < LONG_CALLSIGN_LENGTH; i++)
|
||||
buffer[i + 19U] = your.GetChar(i);
|
||||
|
||||
wxString my1 = header.getMyCall1();
|
||||
for (unsigned int i = 0U; i < my1.Len() && i < LONG_CALLSIGN_LENGTH; i++)
|
||||
buffer[i + 27U] = my1.GetChar(i);
|
||||
|
||||
wxString my2 = header.getMyCall2();
|
||||
for (unsigned int i = 0U; i < my2.Len() && i < SHORT_CALLSIGN_LENGTH; i++)
|
||||
buffer[i + 35U] = my2.GetChar(i);
|
||||
|
||||
wxMutexLocker locker(m_mutex);
|
||||
|
||||
unsigned char data[2U];
|
||||
data[0U] = DSMTT_HEADER;
|
||||
data[1U] = RADIO_HEADER_LENGTH_BYTES - 2U;
|
||||
m_txData.addData(data, 2U);
|
||||
|
||||
m_txData.addData(buffer, RADIO_HEADER_LENGTH_BYTES - 2U);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CGMSKController::writeData(const unsigned char* data, unsigned int, bool end)
|
||||
{
|
||||
bool ret = m_txData.hasSpace(DV_FRAME_LENGTH_BYTES + 2U);
|
||||
if (!ret) {
|
||||
wxLogWarning(wxT("No space to write data"));
|
||||
return false;
|
||||
}
|
||||
|
||||
wxMutexLocker locker(m_mutex);
|
||||
|
||||
unsigned char buffer[2U];
|
||||
buffer[0U] = end ? DSMTT_EOT : DSMTT_DATA;
|
||||
buffer[1U] = DV_FRAME_LENGTH_BYTES;
|
||||
m_txData.addData(buffer, 2U);
|
||||
|
||||
m_txData.addData(data, DV_FRAME_LENGTH_BYTES);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned int CGMSKController::getSpace()
|
||||
{
|
||||
return m_txData.freeSpace() / (DV_FRAME_LENGTH_BYTES + 2U);
|
||||
}
|
||||
|
||||
bool CGMSKController::isTXReady()
|
||||
{
|
||||
if (m_tx)
|
||||
return false;
|
||||
|
||||
return m_txData.isEmpty();
|
||||
}
|
||||
|
||||
bool CGMSKController::reopenModem()
|
||||
{
|
||||
wxLogMessage(wxT("Connection to the GMSK modem has been lost"));
|
||||
|
||||
m_modem->close();
|
||||
|
||||
while (!m_stopped) {
|
||||
bool ret = m_modem->open();
|
||||
if (ret)
|
||||
return true;
|
||||
|
||||
// Reset the drivers state
|
||||
m_mutex.Lock();
|
||||
m_txData.clear();
|
||||
m_mutex.Unlock();
|
||||
|
||||
m_tx = false;
|
||||
|
||||
Sleep(1000UL);
|
||||
}
|
||||
|
||||
delete m_modem;
|
||||
m_modem = NULL;
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2014 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef GMSKController_H
|
||||
#define GMSKController_H
|
||||
|
||||
#include "DStarDefines.h"
|
||||
#include "RingBuffer.h"
|
||||
#include "GMSKModem.h"
|
||||
#include "Modem.h"
|
||||
#include "Utils.h"
|
||||
|
||||
#include <wx/wx.h>
|
||||
|
||||
class CGMSKController : public CModem {
|
||||
public:
|
||||
CGMSKController(USB_INTERFACE iface, unsigned int address, bool duplex);
|
||||
virtual ~CGMSKController();
|
||||
|
||||
virtual void* Entry();
|
||||
|
||||
virtual bool start();
|
||||
|
||||
virtual unsigned int getSpace();
|
||||
virtual bool isTXReady();
|
||||
|
||||
virtual bool writeHeader(const CHeaderData& header);
|
||||
virtual bool writeData(const unsigned char* data, unsigned int length, bool end);
|
||||
|
||||
private:
|
||||
IGMSKModem* m_modem;
|
||||
bool m_duplex;
|
||||
unsigned char* m_buffer;
|
||||
CRingBuffer<unsigned char> m_txData;
|
||||
|
||||
bool reopenModem();
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2013 by Jonathan Naylor, G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "GMSKModem.h"
|
||||
|
||||
IGMSKModem::~IGMSKModem()
|
||||
{
|
||||
}
|
||||
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2014 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef GMSKModem_H
|
||||
#define GMSKModem_H
|
||||
|
||||
#include "Utils.h"
|
||||
|
||||
#include <wx/wx.h>
|
||||
|
||||
class IGMSKModem {
|
||||
public:
|
||||
virtual ~IGMSKModem() = 0;
|
||||
|
||||
virtual bool open() = 0;
|
||||
|
||||
virtual bool readHeader(unsigned char* header, unsigned int length) = 0;
|
||||
virtual int readData(unsigned char* data, unsigned int length, bool& end) = 0;
|
||||
|
||||
virtual TRISTATE getPTT() = 0;
|
||||
virtual void setPTT(bool on) = 0;
|
||||
|
||||
virtual TRISTATE hasSpace() = 0;
|
||||
|
||||
virtual void writeHeader(unsigned char* data, unsigned int length) = 0;
|
||||
virtual int writeData(unsigned char* data, unsigned int length) = 0;
|
||||
|
||||
virtual void close() = 0;
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@ -0,0 +1,443 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2014 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "GMSKModemLibUsb.h"
|
||||
#include "DStarDefines.h"
|
||||
|
||||
const unsigned int VENDOR_ID = 0x04D8U;
|
||||
|
||||
const int USB_TIMEOUT = 1000;
|
||||
|
||||
const int SET_AD_INIT = 0x00;
|
||||
const int SET_PTT = 0x05;
|
||||
const int PUT_DATA = 0x10;
|
||||
const int GET_DATA = 0x11;
|
||||
const int GET_HEADER = 0x21;
|
||||
const int GET_AD_STATUS = 0x30;
|
||||
const int SET_MyCALL = 0x40;
|
||||
const int SET_MyCALL2 = 0x41;
|
||||
const int SET_YourCALL = 0x42;
|
||||
const int SET_RPT1CALL = 0x43;
|
||||
const int SET_RPT2CALL = 0x44;
|
||||
const int SET_FLAGS = 0x45;
|
||||
const int GET_REMAINSPACE = 0x50;
|
||||
const int GET_VERSION = 0xFF;
|
||||
|
||||
const char COS_OnOff = 0x02;
|
||||
const char CRC_ERROR = 0x04;
|
||||
const char LAST_FRAME = 0x08;
|
||||
const char PTT_OnOff = 0x20;
|
||||
|
||||
const int PTT_ON = 1;
|
||||
const int PTT_OFF = 0;
|
||||
|
||||
#if defined(WIN32)
|
||||
const wxString LIBNAME = wxT("libusb0");
|
||||
|
||||
wxDynamicLibrary* CGMSKModemLibUsb::m_library = NULL;
|
||||
bool CGMSKModemLibUsb::m_loaded = false;
|
||||
|
||||
void (*CGMSKModemLibUsb::m_usbInit)() = NULL;
|
||||
int (*CGMSKModemLibUsb::m_usbFindBusses)() = NULL;
|
||||
int (*CGMSKModemLibUsb::m_usbFindDevices)() = NULL;
|
||||
usb_bus* (*CGMSKModemLibUsb::m_usbGetBusses)() = NULL;
|
||||
usb_dev_handle* (*CGMSKModemLibUsb::m_usbOpen)(struct usb_device*) = NULL;
|
||||
int (*CGMSKModemLibUsb::m_usbSetConfiguration)(usb_dev_handle*, int) = NULL;
|
||||
int (*CGMSKModemLibUsb::m_usbControlMsg)(usb_dev_handle*, int, int, int, int, unsigned char*, int, int) = NULL;
|
||||
char* (*CGMSKModemLibUsb::m_usbStrerror)() = NULL;
|
||||
int (*CGMSKModemLibUsb::m_usbClose)(usb_dev_handle*) = NULL;
|
||||
#endif
|
||||
|
||||
static void libUsbLogError(int ret, const char *message) {
|
||||
#if defined(WIN32)
|
||||
wxString errorText(CGMSKModemLibUsb::m_usbStrerror(), wxConvLocal);
|
||||
#else
|
||||
wxString errorText(libusb_error_name(ret), wxConvLocal);
|
||||
#endif
|
||||
wxLogMessage(wxT("%s, ret: %d, err=%s"), message, ret, errorText.c_str());
|
||||
}
|
||||
|
||||
CGMSKModemLibUsb::CGMSKModemLibUsb(unsigned int address) :
|
||||
m_address(address),
|
||||
m_dev(NULL),
|
||||
m_brokenSpace(false)
|
||||
#if !defined(WIN32)
|
||||
,m_context(NULL)
|
||||
#endif
|
||||
{
|
||||
|
||||
#if defined(WIN32)
|
||||
if (m_library == NULL)
|
||||
m_library = new wxDynamicLibrary(LIBNAME);
|
||||
|
||||
if (!m_library->IsLoaded()) {
|
||||
wxLogError(wxT("Unable to load shared library %s"), LIBNAME.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
void* ptr1 = m_library->GetSymbol(wxT("usb_init"));
|
||||
void* ptr2 = m_library->GetSymbol(wxT("usb_find_busses"));
|
||||
void* ptr3 = m_library->GetSymbol(wxT("usb_find_devices"));
|
||||
void* ptr4 = m_library->GetSymbol(wxT("usb_get_busses"));
|
||||
void* ptr5 = m_library->GetSymbol(wxT("usb_open"));
|
||||
void* ptr6 = m_library->GetSymbol(wxT("usb_set_configuration"));
|
||||
void* ptr7 = m_library->GetSymbol(wxT("usb_control_msg"));
|
||||
void* ptr8 = m_library->GetSymbol(wxT("usb_strerror"));
|
||||
void* ptr9 = m_library->GetSymbol(wxT("usb_close"));
|
||||
|
||||
if (ptr1 == NULL || ptr2 == NULL || ptr3 == NULL ||
|
||||
ptr4 == NULL || ptr5 == NULL || ptr6 == NULL ||
|
||||
ptr7 == NULL || ptr8 == NULL || ptr9 == NULL) {
|
||||
wxLogError(wxT("Unable to get symbols from %s"), LIBNAME.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
m_usbInit = (void (*)())ptr1;
|
||||
m_usbFindBusses = (int (*)())ptr2;
|
||||
m_usbFindDevices = (int (*)())ptr3;
|
||||
m_usbGetBusses = (usb_bus* (*)())ptr4;
|
||||
m_usbOpen = (usb_dev_handle* (*)(struct usb_device*))ptr5;
|
||||
m_usbSetConfiguration = (int (*)(usb_dev_handle*, int))ptr6;
|
||||
m_usbControlMsg = (int (*)(usb_dev_handle*, int, int, int, int, unsigned char*, int, int))ptr7;
|
||||
m_usbStrerror = (char* (*)())ptr8;
|
||||
m_usbClose = (int (*)(usb_dev_handle*))ptr9;
|
||||
|
||||
wxLogMessage(wxT("Successfully loaded library %s"), LIBNAME.c_str());
|
||||
|
||||
m_loaded = true;
|
||||
#else
|
||||
::libusb_init(&m_context);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
CGMSKModemLibUsb::~CGMSKModemLibUsb()
|
||||
{
|
||||
#if !defined(WIN32)
|
||||
wxASSERT(m_context != NULL);
|
||||
::libusb_exit(m_context);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CGMSKModemLibUsb::open()
|
||||
{
|
||||
#if !defined(WIN32)
|
||||
wxASSERT(m_context != NULL);
|
||||
#endif
|
||||
wxASSERT(m_dev == NULL);
|
||||
|
||||
bool ret1 = openModem();
|
||||
if (!ret1) {
|
||||
wxLogError(wxT("Cannot find the GMSK Modem with address: 0x%04X"), m_address);
|
||||
return false;
|
||||
}
|
||||
|
||||
wxLogInfo(wxT("Found the GMSK Modem with address: 0x%04X"), m_address);
|
||||
|
||||
wxString version;
|
||||
|
||||
int ret2;
|
||||
do {
|
||||
unsigned char buffer[GMSK_MODEM_DATA_LENGTH];
|
||||
ret2 = io(0xC0, GET_VERSION, 0, 0, buffer, GMSK_MODEM_DATA_LENGTH, USB_TIMEOUT);
|
||||
if (ret2 > 0) {
|
||||
wxString text((char *) buffer, wxConvLocal, ret2);
|
||||
version.Append(text);
|
||||
} else if (ret2 < 0) {
|
||||
::libUsbLogError(ret2, "GET_VERSION");
|
||||
close();
|
||||
return false;
|
||||
}
|
||||
} while (ret2 == int(GMSK_MODEM_DATA_LENGTH));
|
||||
|
||||
wxLogInfo(wxT("Firmware version: %s"), version.c_str());
|
||||
|
||||
// Trap firmware version 0.1.00 of DUTCH*Star and complain loudly
|
||||
if (version.Find(wxT("DUTCH*Star")) != wxNOT_FOUND && version.Find(wxT("0.1.00")) != wxNOT_FOUND) {
|
||||
wxLogWarning(wxT("This modem firmware is not supported by the repeater"));
|
||||
wxLogWarning(wxT("Please upgrade to a newer version"));
|
||||
close();
|
||||
return false;
|
||||
}
|
||||
|
||||
// DUTCH*Star firmware has a broken concept of free space
|
||||
if (version.Find(wxT("DUTCH*Star")) != wxNOT_FOUND)
|
||||
m_brokenSpace = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CGMSKModemLibUsb::readHeader(unsigned char* header, unsigned int length)
|
||||
{
|
||||
wxASSERT(header != NULL);
|
||||
wxASSERT(length > (RADIO_HEADER_LENGTH_BYTES * 2U));
|
||||
|
||||
unsigned int offset = 0U;
|
||||
|
||||
while (offset < RADIO_HEADER_LENGTH_BYTES) {
|
||||
int ret = io(0xC0, GET_HEADER, 0, 0, (header + offset), 8, USB_TIMEOUT);
|
||||
if (ret < 0) {
|
||||
::libUsbLogError(ret, "GET_HEADER");
|
||||
|
||||
if (ret == -19) // -ENODEV
|
||||
return false;
|
||||
|
||||
::wxMilliSleep(10UL);
|
||||
} else if (ret == 0) {
|
||||
if (offset == 0U)
|
||||
return false;
|
||||
|
||||
::wxMilliSleep(10UL);
|
||||
|
||||
unsigned char status;
|
||||
int ret = io(0xC0, GET_AD_STATUS, 0, 0, &status, 1, USB_TIMEOUT);
|
||||
if (ret < 0) {
|
||||
::libUsbLogError(ret, "GET_COS");
|
||||
|
||||
if (ret == -19) // -ENODEV
|
||||
return false;
|
||||
|
||||
::wxMilliSleep(10UL);
|
||||
} else if (ret > 0) {
|
||||
if ((status & COS_OnOff) == COS_OnOff)
|
||||
offset = 0U;
|
||||
}
|
||||
} else {
|
||||
offset += ret;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned char status;
|
||||
int ret = io(0xC0, GET_AD_STATUS, 0, 0, &status, 1, USB_TIMEOUT);
|
||||
if (ret < 0) {
|
||||
::libUsbLogError(ret, "GET_CRC");
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((status & CRC_ERROR) == CRC_ERROR) {
|
||||
wxLogMessage(wxT("Header - CRC Error"));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int CGMSKModemLibUsb::readData(unsigned char* data, unsigned int length, bool& end)
|
||||
{
|
||||
wxASSERT(data != NULL);
|
||||
wxASSERT(length > 0U);
|
||||
|
||||
end = false;
|
||||
|
||||
int ret = io(0xC0, GET_DATA, 0, 0, data, GMSK_MODEM_DATA_LENGTH, USB_TIMEOUT);
|
||||
if (ret < 0) {
|
||||
if (ret == -19) { // -ENODEV
|
||||
::libUsbLogError(ret, "GET_DATA");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
} else if (ret == 0) {
|
||||
unsigned char status;
|
||||
int ret = io(0xC0, GET_AD_STATUS, 0, 0, &status, 1, USB_TIMEOUT);
|
||||
if (ret < 0) {
|
||||
::libUsbLogError(ret, "LAST_FRAME");
|
||||
|
||||
if (ret == -19) // -ENODEV
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((status & LAST_FRAME) == LAST_FRAME)
|
||||
end = true;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void CGMSKModemLibUsb::writeHeader(unsigned char* header, unsigned int length)
|
||||
{
|
||||
wxASSERT(header != NULL);
|
||||
wxASSERT(length >= (RADIO_HEADER_LENGTH_BYTES - 2U));
|
||||
|
||||
io(0x40, SET_MyCALL2, 0, 0, (header + 35U), SHORT_CALLSIGN_LENGTH, USB_TIMEOUT);
|
||||
io(0x40, SET_MyCALL, 0, 0, (header + 27U), LONG_CALLSIGN_LENGTH, USB_TIMEOUT);
|
||||
io(0x40, SET_YourCALL, 0, 0, (header + 19U), LONG_CALLSIGN_LENGTH, USB_TIMEOUT);
|
||||
io(0x40, SET_RPT1CALL, 0, 0, (header + 11U), LONG_CALLSIGN_LENGTH, USB_TIMEOUT);
|
||||
io(0x40, SET_RPT2CALL, 0, 0, (header + 3U), LONG_CALLSIGN_LENGTH, USB_TIMEOUT);
|
||||
io(0x40, SET_FLAGS, 0, 0, (header + 0U), 3U, USB_TIMEOUT);
|
||||
}
|
||||
|
||||
TRISTATE CGMSKModemLibUsb::hasSpace()
|
||||
{
|
||||
unsigned char space;
|
||||
int rc = io(0xC0, GET_REMAINSPACE, 0, 0, &space, 1, USB_TIMEOUT);
|
||||
if (rc != 1) {
|
||||
::libUsbLogError(rc, "GET_REMAINSPACE");
|
||||
return STATE_UNKNOWN;
|
||||
}
|
||||
|
||||
if (space >= DV_FRAME_LENGTH_BYTES)
|
||||
return STATE_TRUE;
|
||||
else
|
||||
return STATE_FALSE;
|
||||
}
|
||||
|
||||
TRISTATE CGMSKModemLibUsb::getPTT()
|
||||
{
|
||||
unsigned char status;
|
||||
int rc = io(0xC0, GET_AD_STATUS, 0, 0, &status, 1, USB_TIMEOUT);
|
||||
if (rc < 1) {
|
||||
::libUsbLogError(rc, "GET_PTT");
|
||||
return STATE_UNKNOWN;
|
||||
}
|
||||
|
||||
if ((status & PTT_OnOff) == PTT_OnOff)
|
||||
return STATE_TRUE;
|
||||
else
|
||||
return STATE_FALSE;
|
||||
}
|
||||
|
||||
void CGMSKModemLibUsb::setPTT(bool on)
|
||||
{
|
||||
unsigned char c;
|
||||
io(0x40, SET_PTT, on ? PTT_ON : PTT_OFF, 0, &c, 0, USB_TIMEOUT);
|
||||
}
|
||||
|
||||
int CGMSKModemLibUsb::writeData(unsigned char* data, unsigned int length)
|
||||
{
|
||||
wxASSERT(data != NULL);
|
||||
wxASSERT(length > 0U && length <= DV_FRAME_LENGTH_BYTES);
|
||||
|
||||
if (length > GMSK_MODEM_DATA_LENGTH) {
|
||||
int ret = io(0x40, PUT_DATA, 0, 0, data, GMSK_MODEM_DATA_LENGTH, USB_TIMEOUT);
|
||||
if (ret < 0) {
|
||||
if (ret == -19) { // -ENODEV
|
||||
::libUsbLogError(ret, "PUT_DATA 1");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Give libUSB some recovery time
|
||||
::wxMilliSleep(3UL);
|
||||
|
||||
ret = io(0x40, PUT_DATA, 0, 0, (data + GMSK_MODEM_DATA_LENGTH), length - GMSK_MODEM_DATA_LENGTH, USB_TIMEOUT);
|
||||
if (ret < 0) {
|
||||
if (ret == -19) { // -ENODEV
|
||||
::libUsbLogError(ret, "PUT_DATA 2");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return int(GMSK_MODEM_DATA_LENGTH);
|
||||
}
|
||||
|
||||
return length;
|
||||
} else {
|
||||
int ret = io(0x40, PUT_DATA, 0, 0, data, length, USB_TIMEOUT);
|
||||
if (ret < 0) {
|
||||
if (ret == -19) { // -ENODEV
|
||||
::libUsbLogError(ret, "PUT_DATA");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
}
|
||||
|
||||
void CGMSKModemLibUsb::close()
|
||||
{
|
||||
wxASSERT(m_dev != NULL);
|
||||
|
||||
#if defined(WIN32)
|
||||
m_usbClose(m_dev);
|
||||
#else
|
||||
libusb_close(m_dev);
|
||||
#endif
|
||||
m_dev = NULL;
|
||||
}
|
||||
|
||||
bool CGMSKModemLibUsb::openModem()
|
||||
{
|
||||
#if defined(WIN32)
|
||||
if (!m_loaded)
|
||||
return false;
|
||||
|
||||
m_usbInit();
|
||||
m_usbFindBusses();
|
||||
m_usbFindDevices();
|
||||
|
||||
for (struct usb_bus* bus = m_usbGetBusses(); bus != NULL; bus = bus->next) {
|
||||
for (struct usb_device* dev = bus->devices; dev != NULL; dev = dev->next) {
|
||||
if (dev->descriptor.idVendor == VENDOR_ID && dev->descriptor.idProduct == m_address) {
|
||||
m_dev = m_usbOpen(dev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_dev == NULL)
|
||||
return false;
|
||||
|
||||
m_usbSetConfiguration(m_dev, 1);
|
||||
#else
|
||||
m_dev = ::libusb_open_device_with_vid_pid(m_context, VENDOR_ID, m_address);
|
||||
if (m_dev == NULL)
|
||||
return false;
|
||||
|
||||
::libusb_set_configuration(m_dev, 1);
|
||||
#endif
|
||||
|
||||
unsigned char c;
|
||||
io(0x40, SET_AD_INIT, 0, 0, &c, 0, USB_TIMEOUT);
|
||||
|
||||
setPTT(false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int CGMSKModemLibUsb::io(uint8_t requestType, uint8_t request, uint16_t value,
|
||||
uint16_t index, unsigned char* data, uint16_t length,
|
||||
unsigned int timeout)
|
||||
{
|
||||
wxASSERT(m_dev != NULL);
|
||||
wxASSERT(data != NULL);
|
||||
|
||||
int ret = 0;
|
||||
for (unsigned int i = 0U; i < 4U; i++) {
|
||||
#if defined(WIN32)
|
||||
ret = m_usbControlMsg(m_dev, requestType, request, value, index, data, length, timeout);
|
||||
#else
|
||||
ret = ::libusb_control_transfer(m_dev, requestType, request, value, index, data, length, timeout);
|
||||
#endif
|
||||
if (ret >= 0)
|
||||
return ret;
|
||||
|
||||
if (ret == -19) // ENODEV
|
||||
return ret;
|
||||
|
||||
::wxMilliSleep(5UL);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2014 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef GMSKModemLibUsb_H
|
||||
#define GMSKModemLibUsb_H
|
||||
|
||||
#include "GMSKModem.h"
|
||||
#include "Utils.h"
|
||||
|
||||
#include <wx/wx.h>
|
||||
#if defined(WIN32)
|
||||
#include <wx/dynlib.h>
|
||||
#if _MSC_VER == 1900
|
||||
#undef __USB_H__
|
||||
#include <lusb0_usb.h>
|
||||
#else
|
||||
#include "lusb0_usb.h"
|
||||
#endif
|
||||
#else
|
||||
#include <libusb-1.0/libusb.h>
|
||||
#endif
|
||||
|
||||
class CGMSKModemLibUsb : public IGMSKModem {
|
||||
public:
|
||||
CGMSKModemLibUsb(unsigned int address);
|
||||
virtual ~CGMSKModemLibUsb();
|
||||
|
||||
virtual bool open();
|
||||
|
||||
virtual bool readHeader(unsigned char* header, unsigned int length);
|
||||
virtual int readData(unsigned char* data, unsigned int length, bool& end);
|
||||
|
||||
virtual TRISTATE getPTT();
|
||||
virtual void setPTT(bool on);
|
||||
|
||||
virtual TRISTATE hasSpace();
|
||||
|
||||
virtual void writeHeader(unsigned char* data, unsigned int length);
|
||||
virtual int writeData(unsigned char* data, unsigned int length);
|
||||
|
||||
virtual void close();
|
||||
#if defined(WIN32)
|
||||
static char* (*m_usbStrerror)();
|
||||
#endif
|
||||
|
||||
private:
|
||||
unsigned int m_address;
|
||||
#if defined(WIN32)
|
||||
struct usb_dev_handle* m_dev;
|
||||
|
||||
static wxDynamicLibrary* m_library;
|
||||
static bool m_loaded;
|
||||
|
||||
static void (*m_usbInit)();
|
||||
static int (*m_usbFindBusses)();
|
||||
static int (*m_usbFindDevices)();
|
||||
static struct usb_bus* (*m_usbGetBusses)();
|
||||
static usb_dev_handle* (*m_usbOpen)(struct usb_device*);
|
||||
static int (*m_usbSetConfiguration)(usb_dev_handle*, int);
|
||||
static int (*m_usbControlMsg)(usb_dev_handle*, int, int, int, int, unsigned char*, int, int);
|
||||
static int (*m_usbClose)(usb_dev_handle*);
|
||||
|
||||
#else
|
||||
libusb_context* m_context;
|
||||
libusb_device_handle* m_dev;
|
||||
|
||||
#endif
|
||||
int io(uint8_t requestType, uint8_t request, uint16_t value, uint16_t index, unsigned char* data, uint16_t length, unsigned int timeout);
|
||||
|
||||
bool m_brokenSpace;
|
||||
|
||||
bool openModem();
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,381 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2014 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "GMSKModemWinUSB.h"
|
||||
#include "DStarDefines.h"
|
||||
|
||||
#include <Setupapi.h>
|
||||
|
||||
const unsigned char SET_AD_INIT = 0x00U;
|
||||
const unsigned char SET_PTT = 0x05U;
|
||||
const unsigned char PUT_DATA = 0x10U;
|
||||
const unsigned char GET_DATA = 0x11U;
|
||||
const unsigned char GET_HEADER = 0x21U;
|
||||
const unsigned char GET_AD_STATUS = 0x30U;
|
||||
const unsigned char SET_MyCALL = 0x40U;
|
||||
const unsigned char SET_MyCALL2 = 0x41U;
|
||||
const unsigned char SET_YourCALL = 0x42U;
|
||||
const unsigned char SET_RPT1CALL = 0x43U;
|
||||
const unsigned char SET_RPT2CALL = 0x44U;
|
||||
const unsigned char SET_FLAGS = 0x45U;
|
||||
const unsigned char GET_REMAINSPACE = 0x50U;
|
||||
const unsigned char GET_VERSION = 0xFFU;
|
||||
|
||||
const unsigned char COS_OnOff = 0x02U;
|
||||
const unsigned char CRC_ERROR = 0x04U;
|
||||
const unsigned char LAST_FRAME = 0x08U;
|
||||
const unsigned char PTT_OnOff = 0x20U;
|
||||
|
||||
const unsigned char PTT_ON = 1U;
|
||||
const unsigned char PTT_OFF = 0U;
|
||||
|
||||
CGMSKModemWinUSB::CGMSKModemWinUSB(unsigned int address) :
|
||||
m_address(address),
|
||||
m_file(INVALID_HANDLE_VALUE),
|
||||
m_handle(INVALID_HANDLE_VALUE),
|
||||
m_brokenSpace(false)
|
||||
{
|
||||
}
|
||||
|
||||
CGMSKModemWinUSB::~CGMSKModemWinUSB()
|
||||
{
|
||||
}
|
||||
|
||||
bool CGMSKModemWinUSB::open()
|
||||
{
|
||||
wxASSERT(m_handle == INVALID_HANDLE_VALUE);
|
||||
|
||||
bool res = openModem();
|
||||
if (!res) {
|
||||
wxLogError(wxT("Cannot find the GMSK Modem with address: 0x%04X"), m_address);
|
||||
return false;
|
||||
}
|
||||
|
||||
wxLogInfo(wxT("Found the GMSK Modem with address: 0x%04X"), m_address);
|
||||
|
||||
wxString version;
|
||||
|
||||
int ret;
|
||||
do {
|
||||
unsigned char buffer[GMSK_MODEM_DATA_LENGTH];
|
||||
ret = io(GET_VERSION, 0xC0U, 0U, buffer, GMSK_MODEM_DATA_LENGTH);
|
||||
if (ret > 0) {
|
||||
wxString text((char*)buffer, wxConvLocal, ret);
|
||||
version.Append(text);
|
||||
} else if (ret < 0) {
|
||||
wxLogError(wxT("GET_VERSION returned %d"), -ret);
|
||||
close();
|
||||
return false;
|
||||
}
|
||||
} while (ret == int(GMSK_MODEM_DATA_LENGTH));
|
||||
|
||||
wxLogInfo(wxT("Firmware version: %s"), version.c_str());
|
||||
|
||||
// Trap firmware version 0.1.00 of DUTCH*Star and complain loudly
|
||||
if (version.Find(wxT("DUTCH*Star")) != wxNOT_FOUND && version.Find(wxT("0.1.00")) != wxNOT_FOUND) {
|
||||
wxLogWarning(wxT("This modem firmware is not supported by the repeater"));
|
||||
wxLogWarning(wxT("Please upgrade to a newer version"));
|
||||
close();
|
||||
return false;
|
||||
}
|
||||
|
||||
// DUTCH*Star firmware has a broken concept of free space
|
||||
if (version.Find(wxT("DUTCH*Star")) != wxNOT_FOUND)
|
||||
m_brokenSpace = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CGMSKModemWinUSB::readHeader(unsigned char* header, unsigned int length)
|
||||
{
|
||||
wxASSERT(header != NULL);
|
||||
wxASSERT(length > (RADIO_HEADER_LENGTH_BYTES * 2U));
|
||||
|
||||
unsigned int offset = 0U;
|
||||
|
||||
while (offset < RADIO_HEADER_LENGTH_BYTES) {
|
||||
int ret = io(GET_HEADER, 0xC0U, 0U, header + offset, GMSK_MODEM_DATA_LENGTH);
|
||||
if (ret < 0) {
|
||||
wxLogError(wxT("GET_HEADER returned %d"), -ret);
|
||||
return false;
|
||||
} else if (ret == 0) {
|
||||
if (offset == 0U)
|
||||
return false;
|
||||
|
||||
::wxMilliSleep(10UL);
|
||||
|
||||
unsigned char status;
|
||||
ret = io(GET_AD_STATUS, 0xC0U, 0U, &status, 1U);
|
||||
if (ret < 0) {
|
||||
wxLogError(wxT("GET_COS returned %d"), -ret);
|
||||
return false;
|
||||
} else if (ret > 0) {
|
||||
if ((status & COS_OnOff) == COS_OnOff)
|
||||
offset = 0U;
|
||||
}
|
||||
} else {
|
||||
offset += ret;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned char status;
|
||||
int ret = io(GET_AD_STATUS, 0xC0U, 0U, &status, 1U);
|
||||
if (ret < 0) {
|
||||
wxLogError(wxT("GET_CRC returned %d"), -ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((status & CRC_ERROR) == CRC_ERROR) {
|
||||
wxLogMessage(wxT("Invalid CRC on header"));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int CGMSKModemWinUSB::readData(unsigned char* data, unsigned int length, bool& end)
|
||||
{
|
||||
wxASSERT(data != NULL);
|
||||
wxASSERT(length > 0U);
|
||||
|
||||
end = false;
|
||||
|
||||
int ret = io(GET_DATA, 0xC0U, 0U, data, GMSK_MODEM_DATA_LENGTH);
|
||||
if (ret < 0) {
|
||||
wxLogError(wxT("GET_DATA returned %d"), -ret);
|
||||
return ret;
|
||||
} else if (ret == 0) {
|
||||
unsigned char status;
|
||||
int ret = io(GET_AD_STATUS, 0xC0U, 0U, &status, 1U);
|
||||
if (ret < 0) {
|
||||
wxLogError(wxT("LAST_FRAME returned %d"), -ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if ((status & LAST_FRAME) == LAST_FRAME)
|
||||
end = true;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void CGMSKModemWinUSB::writeHeader(unsigned char* header, unsigned int length)
|
||||
{
|
||||
wxASSERT(header != NULL);
|
||||
wxASSERT(length >= (RADIO_HEADER_LENGTH_BYTES - 2U));
|
||||
|
||||
io(SET_MyCALL2, 0x40U, 0U, header + 35U, SHORT_CALLSIGN_LENGTH);
|
||||
io(SET_MyCALL, 0x40U, 0U, header + 27U, LONG_CALLSIGN_LENGTH);
|
||||
io(SET_YourCALL, 0x40U, 0U, header + 19U, LONG_CALLSIGN_LENGTH);
|
||||
io(SET_RPT1CALL, 0x40U, 0U, header + 11U, LONG_CALLSIGN_LENGTH);
|
||||
io(SET_RPT2CALL, 0x40U, 0U, header + 3U, LONG_CALLSIGN_LENGTH);
|
||||
io(SET_FLAGS, 0x40U, 0U, header + 0U, 3U);
|
||||
}
|
||||
|
||||
TRISTATE CGMSKModemWinUSB::getPTT()
|
||||
{
|
||||
unsigned char status;
|
||||
int ret = io(GET_AD_STATUS, 0xC0U, 0U, &status, 1U);
|
||||
if (ret != 1) {
|
||||
wxLogError(wxT("GET_PTT returned %d"), -ret);
|
||||
return STATE_UNKNOWN;
|
||||
}
|
||||
|
||||
if ((status & PTT_OnOff) == PTT_OnOff)
|
||||
return STATE_TRUE;
|
||||
else
|
||||
return STATE_FALSE;
|
||||
}
|
||||
|
||||
void CGMSKModemWinUSB::setPTT(bool on)
|
||||
{
|
||||
unsigned char c;
|
||||
io(SET_PTT, 0x40U, on ? PTT_ON : PTT_OFF, &c, 0U);
|
||||
}
|
||||
|
||||
TRISTATE CGMSKModemWinUSB::hasSpace()
|
||||
{
|
||||
unsigned char space;
|
||||
int ret = io(GET_REMAINSPACE, 0xC0U, 0U, &space, 1U);
|
||||
if (ret != 1) {
|
||||
wxLogError(wxT("GET_REMAINSPACE returned %d"), -ret);
|
||||
return STATE_UNKNOWN;
|
||||
}
|
||||
|
||||
if (space >= DV_FRAME_LENGTH_BYTES)
|
||||
return STATE_TRUE;
|
||||
else
|
||||
return STATE_FALSE;
|
||||
}
|
||||
|
||||
int CGMSKModemWinUSB::writeData(unsigned char* data, unsigned int length)
|
||||
{
|
||||
wxASSERT(data != NULL);
|
||||
wxASSERT(length > 0U && length <= DV_FRAME_LENGTH_BYTES);
|
||||
|
||||
if (length > GMSK_MODEM_DATA_LENGTH) {
|
||||
int ret = io(PUT_DATA, 0x40U, 0U, data, GMSK_MODEM_DATA_LENGTH);
|
||||
if (ret < 0) {
|
||||
if (ret == -22) {
|
||||
wxLogError(wxT("PUT_DATA 1, returned %d"), -ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = io(PUT_DATA, 0x40U, 0U, data + GMSK_MODEM_DATA_LENGTH, length - GMSK_MODEM_DATA_LENGTH);
|
||||
if (ret < 0) {
|
||||
if (ret == -22) {
|
||||
wxLogError(wxT("PUT_DATA 2, returned %d"), -ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return int(GMSK_MODEM_DATA_LENGTH);
|
||||
}
|
||||
|
||||
return length;
|
||||
} else {
|
||||
int ret = io(PUT_DATA, 0x40U, 0U, data, length);
|
||||
if (ret < 0) {
|
||||
if (ret == -22) {
|
||||
wxLogError(wxT("PUT_DATA returned %d"), -ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
}
|
||||
|
||||
void CGMSKModemWinUSB::close()
|
||||
{
|
||||
wxASSERT(m_file != INVALID_HANDLE_VALUE);
|
||||
wxASSERT(m_handle != INVALID_HANDLE_VALUE);
|
||||
|
||||
::CloseHandle(m_file);
|
||||
::WinUsb_Free(m_handle);
|
||||
|
||||
m_file = INVALID_HANDLE_VALUE;
|
||||
m_handle = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
bool CGMSKModemWinUSB::openModem()
|
||||
{
|
||||
WCHAR id1[15U], id2[15U];
|
||||
::swprintf(id1, L"pid_%04x", m_address);
|
||||
::swprintf(id2, L"pid_%04X", m_address);
|
||||
|
||||
wxString wxId1(id1, wxConvLocal);
|
||||
wxString wxId2(id2, wxConvLocal);
|
||||
|
||||
CLSID clsId;
|
||||
LPOLESTR str = OLESTR("{136C76EF-3F4E-4030-A7E3-E1003EF0A715}");
|
||||
HRESULT result = ::CLSIDFromString(str, &clsId);
|
||||
if (result != NOERROR) {
|
||||
wxLogError(wxT("Error from CLSIDFromString: err=%lu"), ::GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
HDEVINFO devInfo = ::SetupDiGetClassDevs(&clsId, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
|
||||
if (devInfo == INVALID_HANDLE_VALUE) {
|
||||
wxLogError(wxT("Error from SetupDiGetClassDevs: err=%lu"), ::GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
SP_DEVICE_INTERFACE_DATA devInfoData;
|
||||
devInfoData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
|
||||
|
||||
for (unsigned int index = 0U; ::SetupDiEnumDeviceInterfaces(devInfo, NULL, &clsId, index, &devInfoData); index++) {
|
||||
DWORD length;
|
||||
::SetupDiGetDeviceInterfaceDetail(devInfo, &devInfoData, NULL, 0U, &length, NULL);
|
||||
|
||||
PSP_DEVICE_INTERFACE_DETAIL_DATA detailData = PSP_DEVICE_INTERFACE_DETAIL_DATA(::malloc(length));
|
||||
detailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
|
||||
|
||||
DWORD required;
|
||||
BOOL ret1 = ::SetupDiGetDeviceInterfaceDetail(devInfo, &devInfoData, detailData, length, &required, NULL);
|
||||
if (!ret1) {
|
||||
wxLogError(wxT("Error from SetupDiGetDeviceInterfaceDetail: err=%lu"), ::GetLastError());
|
||||
::SetupDiDestroyDeviceInfoList(devInfo);
|
||||
::free(detailData);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check the name to see if it's the correct vendor id and address
|
||||
if (::wcsstr(detailData->DevicePath, id1) == NULL && ::wcsstr(detailData->DevicePath, id2) == NULL) {
|
||||
::free(detailData);
|
||||
continue;
|
||||
}
|
||||
|
||||
m_file = ::CreateFile(detailData->DevicePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
|
||||
if (m_file == INVALID_HANDLE_VALUE) {
|
||||
wxLogError(wxT("Error from CreateFile: err=%lu"), ::GetLastError());
|
||||
::SetupDiDestroyDeviceInfoList(devInfo);
|
||||
::free(detailData);
|
||||
return false;
|
||||
}
|
||||
|
||||
ret1 = ::WinUsb_Initialize(m_file, &m_handle);
|
||||
if (!ret1) {
|
||||
wxLogError(wxT("Error from WinUsb_Initialize: err=%lu"), ::GetLastError());
|
||||
::SetupDiDestroyDeviceInfoList(devInfo);
|
||||
::CloseHandle(m_file);
|
||||
::free(detailData);
|
||||
return false;
|
||||
}
|
||||
|
||||
::SetupDiDestroyDeviceInfoList(devInfo);
|
||||
::free(detailData);
|
||||
|
||||
unsigned char c;
|
||||
io(SET_AD_INIT, 0x40U, 0U, &c, 0U);
|
||||
|
||||
setPTT(false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
::SetupDiDestroyDeviceInfoList(devInfo);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int CGMSKModemWinUSB::io(unsigned char type, unsigned char n1, unsigned char n2, unsigned char* buffer, unsigned int length)
|
||||
{
|
||||
wxASSERT(m_handle != INVALID_HANDLE_VALUE);
|
||||
|
||||
WINUSB_SETUP_PACKET packet;
|
||||
packet.RequestType = n1;
|
||||
packet.Request = type;
|
||||
packet.Value = n2;
|
||||
packet.Index = 0U;
|
||||
packet.Length = length;
|
||||
|
||||
ULONG transferred;
|
||||
BOOL ret = ::WinUsb_ControlTransfer(m_handle, packet, buffer, length, &transferred, 0);
|
||||
if (!ret) {
|
||||
long error = ::GetLastError();
|
||||
return -error;
|
||||
}
|
||||
|
||||
return transferred;
|
||||
}
|
||||
|
||||
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2014 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef GMSKModemWinUSB_H
|
||||
#define GMSKModemWinUSB_H
|
||||
|
||||
#include "GMSKModem.h"
|
||||
#include "Utils.h"
|
||||
|
||||
#include <wx/wx.h>
|
||||
|
||||
#include "winusb.h"
|
||||
|
||||
class CGMSKModemWinUSB : public IGMSKModem {
|
||||
public:
|
||||
CGMSKModemWinUSB(unsigned int address);
|
||||
virtual ~CGMSKModemWinUSB();
|
||||
|
||||
virtual bool open();
|
||||
|
||||
virtual bool readHeader(unsigned char* header, unsigned int length);
|
||||
virtual int readData(unsigned char* data, unsigned int length, bool& end);
|
||||
|
||||
virtual TRISTATE getPTT();
|
||||
virtual void setPTT(bool on);
|
||||
|
||||
virtual TRISTATE hasSpace();
|
||||
|
||||
virtual void writeHeader(unsigned char* data, unsigned int length);
|
||||
virtual int writeData(unsigned char* data, unsigned int length);
|
||||
|
||||
virtual void close();
|
||||
|
||||
private:
|
||||
unsigned int m_address;
|
||||
HANDLE m_file;
|
||||
WINUSB_INTERFACE_HANDLE m_handle;
|
||||
bool m_brokenSpace;
|
||||
|
||||
bool openModem();
|
||||
int io(unsigned char type, unsigned char n1, unsigned char n2, unsigned char* buffer, unsigned int length);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@ -0,0 +1,170 @@
|
||||
/*
|
||||
* Copyright (C) 2012,2013,2015 by Jonathan Naylor, G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "GPIOController.h"
|
||||
|
||||
CGPIOController::CGPIOController(unsigned int config) :
|
||||
m_config(config),
|
||||
m_outp1(false),
|
||||
m_outp2(false),
|
||||
m_outp3(false),
|
||||
m_outp4(false),
|
||||
m_outp5(false),
|
||||
m_outp6(false),
|
||||
m_outp7(false),
|
||||
m_outp8(false)
|
||||
{
|
||||
}
|
||||
|
||||
CGPIOController::~CGPIOController()
|
||||
{
|
||||
}
|
||||
|
||||
#include <wiringPi.h>
|
||||
|
||||
bool CGPIOController::open()
|
||||
{
|
||||
bool ret = ::wiringPiSetup() != -1;
|
||||
if (!ret) {
|
||||
wxLogError(wxT("Unable to initialise wiringPi"));
|
||||
return false;
|
||||
}
|
||||
|
||||
::pinMode(8, INPUT);
|
||||
::pinMode(9, INPUT);
|
||||
::pinMode(7, INPUT);
|
||||
::pinMode(0, INPUT);
|
||||
::pinMode(2, INPUT);
|
||||
|
||||
// Set pull ups on the input pins
|
||||
::pullUpDnControl(8, PUD_UP);
|
||||
::pullUpDnControl(9, PUD_UP);
|
||||
::pullUpDnControl(7, PUD_UP);
|
||||
::pullUpDnControl(0, PUD_UP);
|
||||
::pullUpDnControl(2, PUD_UP);
|
||||
|
||||
::pinMode(12, OUTPUT);
|
||||
::pinMode(13, OUTPUT);
|
||||
::pinMode(14, OUTPUT);
|
||||
::pinMode(11, OUTPUT);
|
||||
::pinMode(10, OUTPUT);
|
||||
::pinMode(6, OUTPUT);
|
||||
::pinMode(5, OUTPUT);
|
||||
::pinMode(4, OUTPUT);
|
||||
|
||||
setDigitalOutputs(false, false, false, false, false, false, false, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CGPIOController::getDigitalInputs(bool& inp1, bool& inp2, bool& inp3, bool& inp4, bool& inp5)
|
||||
{
|
||||
inp1 = ::digitalRead(8) == LOW;
|
||||
|
||||
inp2 = ::digitalRead(9) == LOW;
|
||||
|
||||
inp3 = ::digitalRead(7) == LOW;
|
||||
|
||||
inp4 = ::digitalRead(0) == LOW;
|
||||
|
||||
inp5 = ::digitalRead(2) == LOW;
|
||||
}
|
||||
|
||||
void CGPIOController::setDigitalOutputs(bool outp1, bool outp2, bool outp3, bool outp4, bool outp5, bool outp6, bool outp7, bool outp8)
|
||||
{
|
||||
if (m_config == 1U) {
|
||||
if (outp1 != m_outp1) {
|
||||
::digitalWrite(12, outp1 ? HIGH : LOW);
|
||||
m_outp1 = outp1;
|
||||
}
|
||||
|
||||
if (outp2 != m_outp2) {
|
||||
::digitalWrite(13, outp2 ? HIGH : LOW);
|
||||
m_outp2 = outp2;
|
||||
}
|
||||
|
||||
if (outp3 != m_outp3) {
|
||||
::digitalWrite(14, outp3 ? HIGH : LOW);
|
||||
m_outp3 = outp3;
|
||||
}
|
||||
|
||||
if (outp4 != m_outp4) {
|
||||
::digitalWrite(11, outp4 ? HIGH : LOW);
|
||||
m_outp4 = outp4;
|
||||
}
|
||||
|
||||
if (outp5 != m_outp5) {
|
||||
::digitalWrite(10, outp5 ? HIGH : LOW);
|
||||
m_outp5 = outp5;
|
||||
}
|
||||
|
||||
if (outp6 != m_outp6) {
|
||||
::digitalWrite(6, outp6 ? HIGH : LOW);
|
||||
m_outp6 = outp6;
|
||||
}
|
||||
|
||||
if (outp7 != m_outp7) {
|
||||
::digitalWrite(5, outp7 ? HIGH : LOW);
|
||||
m_outp7 = outp7;
|
||||
}
|
||||
|
||||
if (outp8 != m_outp8) {
|
||||
::digitalWrite(4, outp8 ? HIGH : LOW);
|
||||
m_outp8 = outp8;
|
||||
}
|
||||
} else {
|
||||
if (outp1 != m_outp1) {
|
||||
::digitalWrite(4, outp1 ? HIGH : LOW);
|
||||
m_outp1 = outp1;
|
||||
}
|
||||
|
||||
if (outp2 != m_outp2) {
|
||||
::digitalWrite(5, outp2 ? HIGH : LOW);
|
||||
m_outp2 = outp2;
|
||||
}
|
||||
|
||||
if (outp3 != m_outp3) {
|
||||
::digitalWrite(6, outp3 ? HIGH : LOW);
|
||||
m_outp3 = outp3;
|
||||
}
|
||||
|
||||
if (outp4 != m_outp4) {
|
||||
::digitalWrite(10, outp4 ? HIGH : LOW);
|
||||
m_outp4 = outp4;
|
||||
}
|
||||
|
||||
if (outp5 != m_outp5) {
|
||||
::digitalWrite(11, outp5 ? HIGH : LOW);
|
||||
m_outp5 = outp5;
|
||||
}
|
||||
|
||||
if (outp6 != m_outp6) {
|
||||
::digitalWrite(14, outp6 ? HIGH : LOW);
|
||||
m_outp6 = outp6;
|
||||
}
|
||||
|
||||
if (outp7 != m_outp7) {
|
||||
::digitalWrite(13, outp7 ? HIGH : LOW);
|
||||
m_outp7 = outp7;
|
||||
}
|
||||
|
||||
if (outp8 != m_outp8) {
|
||||
::digitalWrite(12, outp8 ? HIGH : LOW);
|
||||
m_outp8 = outp8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CGPIOController::close()
|
||||
{
|
||||
}
|
||||
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2012,2015 by Jonathan Naylor, G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef GPIOController_H
|
||||
#define GPIOController_H
|
||||
|
||||
#include "HardwareController.h"
|
||||
|
||||
#include <wx/wx.h>
|
||||
|
||||
class CGPIOController : public IHardwareController {
|
||||
public:
|
||||
CGPIOController(unsigned int config);
|
||||
virtual ~CGPIOController();
|
||||
|
||||
virtual bool open();
|
||||
|
||||
virtual void getDigitalInputs(bool& inp1, bool& inp2, bool& inp3, bool& inp4, bool& inp5);
|
||||
|
||||
virtual void setDigitalOutputs(bool outp1, bool outp2, bool outp3, bool outp4, bool outp5, bool outp6, bool outp7, bool outp8);
|
||||
|
||||
virtual void close();
|
||||
|
||||
private:
|
||||
unsigned int m_config;
|
||||
bool m_outp1;
|
||||
bool m_outp2;
|
||||
bool m_outp3;
|
||||
bool m_outp4;
|
||||
bool m_outp5;
|
||||
bool m_outp6;
|
||||
bool m_outp7;
|
||||
bool m_outp8;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,238 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2014 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "GatewayProtocolHandler.h"
|
||||
#include "CCITTChecksumReverse.h"
|
||||
#include "DStarDefines.h"
|
||||
#include "Utils.h"
|
||||
|
||||
// #define DUMP_TX
|
||||
|
||||
const unsigned int BUFFER_LENGTH = 255U;
|
||||
|
||||
CGatewayProtocolHandler::CGatewayProtocolHandler(const wxString& localAddress, unsigned int localPort) :
|
||||
m_socket(localAddress, localPort),
|
||||
m_type(NETWORK_NONE),
|
||||
m_buffer(NULL),
|
||||
m_length(0U)
|
||||
{
|
||||
m_buffer = new unsigned char[BUFFER_LENGTH];
|
||||
|
||||
wxDateTime now = wxDateTime::UNow();
|
||||
::srand(now.GetMillisecond());
|
||||
}
|
||||
|
||||
CGatewayProtocolHandler::~CGatewayProtocolHandler()
|
||||
{
|
||||
delete[] m_buffer;
|
||||
}
|
||||
|
||||
bool CGatewayProtocolHandler::open()
|
||||
{
|
||||
return m_socket.open();
|
||||
}
|
||||
|
||||
bool CGatewayProtocolHandler::writeHeader(const unsigned char* header, wxUint16 id, const in_addr& address, unsigned int port)
|
||||
{
|
||||
unsigned char buffer[50U];
|
||||
|
||||
buffer[0] = 'D';
|
||||
buffer[1] = 'S';
|
||||
buffer[2] = 'R';
|
||||
buffer[3] = 'P';
|
||||
|
||||
buffer[4] = 0x20U;
|
||||
|
||||
buffer[5] = id / 256U; // Unique session id
|
||||
buffer[6] = id % 256U;
|
||||
|
||||
buffer[7] = 0U;
|
||||
|
||||
::memcpy(buffer + 8U, header + 0U, RADIO_HEADER_LENGTH_BYTES - 2U);
|
||||
|
||||
// Get the checksum for the header
|
||||
CCCITTChecksumReverse csum;
|
||||
csum.update(buffer + 8U, RADIO_HEADER_LENGTH_BYTES - 2U);
|
||||
csum.result(buffer + 8U + RADIO_HEADER_LENGTH_BYTES - 2U);
|
||||
|
||||
#if defined(DUMP_TX)
|
||||
CUtils::dump(wxT("Sending Header"), buffer, 49U);
|
||||
#endif
|
||||
|
||||
for (unsigned int i = 0U; i < 4U; i++) {
|
||||
bool ret = m_socket.write(buffer, 49U, address, port);
|
||||
if (!ret)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CGatewayProtocolHandler::writeData(const unsigned char* data, unsigned int length, wxUint16 id, wxUint8 seqNo, const in_addr& address, unsigned int port)
|
||||
{
|
||||
wxASSERT(data != NULL);
|
||||
wxASSERT(length == DV_FRAME_LENGTH_BYTES || length == DV_FRAME_MAX_LENGTH_BYTES);
|
||||
|
||||
unsigned char buffer[30U];
|
||||
|
||||
buffer[0] = 'D';
|
||||
buffer[1] = 'S';
|
||||
buffer[2] = 'R';
|
||||
buffer[3] = 'P';
|
||||
|
||||
buffer[4] = 0x21U;
|
||||
|
||||
buffer[5] = id / 256U; // Unique session id
|
||||
buffer[6] = id % 256U;
|
||||
|
||||
buffer[7] = seqNo;
|
||||
|
||||
buffer[8] = 0U;
|
||||
|
||||
::memcpy(buffer + 9U, data, length);
|
||||
|
||||
#if defined(DUMP_TX)
|
||||
CUtils::dump(wxT("Sending Data"), buffer, length + 9U);
|
||||
#endif
|
||||
|
||||
return m_socket.write(buffer, length + 9U, address, port);
|
||||
}
|
||||
|
||||
NETWORK_TYPE CGatewayProtocolHandler::read(wxUint16& id, in_addr& address, unsigned int& port)
|
||||
{
|
||||
bool res = true;
|
||||
|
||||
// Loop until we have no more data from the socket or we have data for the higher layers
|
||||
while (res)
|
||||
res = readPackets(id, address, port);
|
||||
|
||||
return m_type;
|
||||
}
|
||||
|
||||
bool CGatewayProtocolHandler::readPackets(wxUint16& id, in_addr& address, unsigned int& port)
|
||||
{
|
||||
m_type = NETWORK_NONE;
|
||||
|
||||
// No more data?
|
||||
int length = m_socket.read(m_buffer, BUFFER_LENGTH, address, port);
|
||||
if (length <= 0)
|
||||
return false;
|
||||
|
||||
m_length = length;
|
||||
|
||||
// Invalid packet type?
|
||||
if (m_buffer[0] == 'D' && m_buffer[1] == 'S' && m_buffer[2] == 'R' && m_buffer[3] == 'P') {
|
||||
// Header data
|
||||
if (m_buffer[4] == 0x20U) {
|
||||
id = m_buffer[5U] * 256U + m_buffer[6U];
|
||||
m_type = NETWORK_HEADER;
|
||||
return false;
|
||||
}
|
||||
|
||||
// User data
|
||||
else if (m_buffer[4] == 0x21U) {
|
||||
id = m_buffer[5U] * 256U + m_buffer[6U];
|
||||
m_type = NETWORK_DATA;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Register data
|
||||
else if (m_buffer[4] == 0x0BU) {
|
||||
m_type = NETWORK_REGISTER;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
CUtils::dump(wxT("Unknown packet from the Repeater"), m_buffer, m_length);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned int CGatewayProtocolHandler::readHeader(unsigned char* buffer, unsigned int length)
|
||||
{
|
||||
if (m_type != NETWORK_HEADER)
|
||||
return 0U;
|
||||
|
||||
// If the checksum is 0xFFFF then we accept the header without testing the checksum
|
||||
if (m_buffer[47U] == 0xFFU && m_buffer[48U] == 0xFFU) {
|
||||
::memcpy(buffer, m_buffer + 8U, RADIO_HEADER_LENGTH_BYTES);
|
||||
return RADIO_HEADER_LENGTH_BYTES;
|
||||
}
|
||||
|
||||
// Get the checksum for the header
|
||||
CCCITTChecksumReverse csum;
|
||||
csum.update(m_buffer + 8U, RADIO_HEADER_LENGTH_BYTES - 2U);
|
||||
|
||||
bool check = csum.check(m_buffer + 8U + RADIO_HEADER_LENGTH_BYTES - 2U);
|
||||
if (!check) {
|
||||
CUtils::dump(wxT("Header checksum failure from the Repeater"), m_buffer + 8U, RADIO_HEADER_LENGTH_BYTES);
|
||||
return 0U;
|
||||
}
|
||||
|
||||
::memcpy(buffer, m_buffer + 8U, RADIO_HEADER_LENGTH_BYTES);
|
||||
|
||||
return RADIO_HEADER_LENGTH_BYTES;
|
||||
}
|
||||
|
||||
unsigned int CGatewayProtocolHandler::readData(unsigned char* buffer, unsigned int length, wxUint8& seqNo, unsigned int& errors)
|
||||
{
|
||||
if (m_type != NETWORK_DATA)
|
||||
return 0U;
|
||||
|
||||
unsigned int dataLen = m_length - 9U;
|
||||
|
||||
// Is our buffer too small?
|
||||
if (dataLen > length)
|
||||
dataLen = length;
|
||||
|
||||
seqNo = m_buffer[7U];
|
||||
|
||||
errors = m_buffer[8U];
|
||||
|
||||
::memcpy(buffer, m_buffer + 9U, dataLen);
|
||||
|
||||
// Simple sanity checks of the incoming sync bits
|
||||
if (seqNo == 0U) {
|
||||
// Regenerate sync bytes
|
||||
buffer[9U] = DATA_SYNC_BYTES[0U];
|
||||
buffer[10U] = DATA_SYNC_BYTES[1U];
|
||||
buffer[11U] = DATA_SYNC_BYTES[2U];
|
||||
} else if (::memcmp(buffer + 9U, DATA_SYNC_BYTES, DATA_FRAME_LENGTH_BYTES) == 0) {
|
||||
// Sync bytes appearing where they shouldn't!
|
||||
buffer[9U] = 0x70U;
|
||||
buffer[10U] = 0x4FU;
|
||||
buffer[11U] = 0x93U;
|
||||
}
|
||||
|
||||
return dataLen;
|
||||
}
|
||||
|
||||
unsigned int CGatewayProtocolHandler::readRegister(wxString& name)
|
||||
{
|
||||
if (m_type != NETWORK_REGISTER)
|
||||
return 0U;
|
||||
|
||||
name = wxString((char*)(m_buffer + 5U), wxConvLocal);
|
||||
|
||||
return m_length - 6U;
|
||||
}
|
||||
|
||||
void CGatewayProtocolHandler::close()
|
||||
{
|
||||
m_socket.close();
|
||||
}
|
||||
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2014 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef GatewayProtocolHander_H
|
||||
#define GatewayProtocolHander_H
|
||||
|
||||
#include "UDPReaderWriter.h"
|
||||
#include "DStarDefines.h"
|
||||
|
||||
#include <wx/wx.h>
|
||||
#include <wx/datetime.h>
|
||||
|
||||
class CGatewayProtocolHandler {
|
||||
public:
|
||||
CGatewayProtocolHandler(const wxString& localAddress, unsigned int localPort);
|
||||
~CGatewayProtocolHandler();
|
||||
|
||||
bool open();
|
||||
|
||||
bool writeHeader(const unsigned char* header, wxUint16 id, const in_addr& address, unsigned int port);
|
||||
bool writeData(const unsigned char* data, unsigned int length, wxUint16 id, wxUint8 seqNo, const in_addr& address, unsigned int port);
|
||||
|
||||
NETWORK_TYPE read(wxUint16& id, in_addr& address, unsigned int& port);
|
||||
unsigned int readHeader(unsigned char* data, unsigned int length);
|
||||
unsigned int readData(unsigned char* data, unsigned int length, wxUint8& seqNo, unsigned int& errors);
|
||||
unsigned int readRegister(wxString& name);
|
||||
|
||||
void close();
|
||||
|
||||
private:
|
||||
CUDPReaderWriter m_socket;
|
||||
NETWORK_TYPE m_type;
|
||||
unsigned char* m_buffer;
|
||||
unsigned int m_length;
|
||||
|
||||
bool readPackets(wxUint16& id, in_addr& address, unsigned int& port);
|
||||
};
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C) 2010 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef Golay_H
|
||||
#define Golay_H
|
||||
|
||||
class CGolay {
|
||||
public:
|
||||
static unsigned int encode23127(unsigned int data);
|
||||
static unsigned int encode24128(unsigned int data);
|
||||
|
||||
static unsigned int decode23127(unsigned int code);
|
||||
static unsigned int decode24128(unsigned int code);
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2013 by Jonathan Naylor, G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "HardwareController.h"
|
||||
|
||||
IHardwareController::~IHardwareController()
|
||||
{
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2009,2013 by Jonathan Naylor, G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef HardwareController_H
|
||||
#define HardwareController_H
|
||||
|
||||
class IHardwareController {
|
||||
public:
|
||||
virtual ~IHardwareController() = 0;
|
||||
|
||||
virtual bool open() = 0;
|
||||
|
||||
virtual void getDigitalInputs(bool& inp1, bool& inp2, bool& inp3, bool& inp4, bool& inp5) = 0;
|
||||
|
||||
virtual void setDigitalOutputs(bool outp1, bool outp2, bool outp3, bool outp4, bool outp5, bool outp6, bool outp7, bool outp8) = 0;
|
||||
|
||||
virtual void close() = 0;
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,350 @@
|
||||
/*
|
||||
* Copyright (C) 2009,2011,2013 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "CCITTChecksumReverse.h"
|
||||
#include "HeaderData.h"
|
||||
#include "DStarDefines.h"
|
||||
|
||||
CHeaderData::CHeaderData() :
|
||||
m_time(),
|
||||
m_myCall1(),
|
||||
m_myCall2(),
|
||||
m_yourCall(),
|
||||
m_rptCall1(),
|
||||
m_rptCall2(),
|
||||
m_flag1(0x00),
|
||||
m_flag2(0x00),
|
||||
m_flag3(0x00),
|
||||
m_valid(false)
|
||||
{
|
||||
}
|
||||
|
||||
CHeaderData::CHeaderData(const CHeaderData& header) :
|
||||
m_time(header.m_time),
|
||||
m_myCall1(header.m_myCall1),
|
||||
m_myCall2(header.m_myCall2),
|
||||
m_yourCall(header.m_yourCall),
|
||||
m_rptCall1(header.m_rptCall1),
|
||||
m_rptCall2(header.m_rptCall2),
|
||||
m_flag1(header.m_flag1),
|
||||
m_flag2(header.m_flag2),
|
||||
m_flag3(header.m_flag3),
|
||||
m_valid(header.m_valid)
|
||||
{
|
||||
}
|
||||
|
||||
CHeaderData::CHeaderData(const unsigned char* data, unsigned int length, bool check) :
|
||||
m_time(),
|
||||
m_myCall1(),
|
||||
m_myCall2(),
|
||||
m_yourCall(),
|
||||
m_rptCall1(),
|
||||
m_rptCall2(),
|
||||
m_flag1(0x00),
|
||||
m_flag2(0x00),
|
||||
m_flag3(0x00),
|
||||
m_valid(true)
|
||||
{
|
||||
wxASSERT(data != NULL);
|
||||
wxASSERT(length >= (RADIO_HEADER_LENGTH_BYTES - 2U));
|
||||
|
||||
const unsigned char* p = data;
|
||||
m_flag1 = *p++;
|
||||
m_flag2 = *p++;
|
||||
m_flag3 = *p++;
|
||||
|
||||
m_rptCall2 = wxString((const char*)p, wxConvLocal, LONG_CALLSIGN_LENGTH);
|
||||
p += LONG_CALLSIGN_LENGTH;
|
||||
|
||||
m_rptCall1 = wxString((const char*)p, wxConvLocal, LONG_CALLSIGN_LENGTH);
|
||||
p += LONG_CALLSIGN_LENGTH;
|
||||
|
||||
m_yourCall = wxString((const char*)p, wxConvLocal, LONG_CALLSIGN_LENGTH);
|
||||
p += LONG_CALLSIGN_LENGTH;
|
||||
|
||||
m_myCall1 = wxString((const char*)p, wxConvLocal, LONG_CALLSIGN_LENGTH);
|
||||
p += LONG_CALLSIGN_LENGTH;
|
||||
|
||||
m_myCall2 = wxString((const char*)p, wxConvLocal, SHORT_CALLSIGN_LENGTH);
|
||||
|
||||
m_time.SetToCurrent();
|
||||
|
||||
// We have a checksum, check it if asked
|
||||
if (length >= RADIO_HEADER_LENGTH_BYTES && check) {
|
||||
CCCITTChecksumReverse cksum;
|
||||
|
||||
cksum.update(data, RADIO_HEADER_LENGTH_BYTES - 2U);
|
||||
|
||||
m_valid = cksum.check(data + RADIO_HEADER_LENGTH_BYTES - 2U);
|
||||
}
|
||||
}
|
||||
|
||||
CHeaderData::CHeaderData(const wxString& myCall1, const wxString& myCall2, const wxString& yourCall,
|
||||
const wxString& rptCall1, const wxString& rptCall2, unsigned char flag1,
|
||||
unsigned char flag2, unsigned char flag3) :
|
||||
m_time(),
|
||||
m_myCall1(myCall1),
|
||||
m_myCall2(myCall2),
|
||||
m_yourCall(yourCall),
|
||||
m_rptCall1(rptCall1),
|
||||
m_rptCall2(rptCall2),
|
||||
m_flag1(flag1),
|
||||
m_flag2(flag2),
|
||||
m_flag3(flag3),
|
||||
m_valid(true)
|
||||
{
|
||||
m_time.SetToCurrent();
|
||||
|
||||
m_myCall1.Append(wxT(' '), LONG_CALLSIGN_LENGTH);
|
||||
m_myCall2.Append(wxT(' '), SHORT_CALLSIGN_LENGTH);
|
||||
m_yourCall.Append(wxT(' '), LONG_CALLSIGN_LENGTH);
|
||||
m_rptCall1.Append(wxT(' '), LONG_CALLSIGN_LENGTH);
|
||||
m_rptCall2.Append(wxT(' '), LONG_CALLSIGN_LENGTH);
|
||||
|
||||
m_myCall1.Truncate(LONG_CALLSIGN_LENGTH);
|
||||
m_myCall2.Truncate(SHORT_CALLSIGN_LENGTH);
|
||||
m_yourCall.Truncate(LONG_CALLSIGN_LENGTH);
|
||||
m_rptCall1.Truncate(LONG_CALLSIGN_LENGTH);
|
||||
m_rptCall2.Truncate(LONG_CALLSIGN_LENGTH);
|
||||
}
|
||||
|
||||
CHeaderData::~CHeaderData()
|
||||
{
|
||||
}
|
||||
|
||||
wxDateTime CHeaderData::getTime() const
|
||||
{
|
||||
return m_time;
|
||||
}
|
||||
|
||||
wxString CHeaderData::getMyCall1() const
|
||||
{
|
||||
return m_myCall1;
|
||||
}
|
||||
|
||||
wxString CHeaderData::getMyCall2() const
|
||||
{
|
||||
return m_myCall2;
|
||||
}
|
||||
|
||||
wxString CHeaderData::getYourCall() const
|
||||
{
|
||||
return m_yourCall;
|
||||
}
|
||||
|
||||
wxString CHeaderData::getRptCall1() const
|
||||
{
|
||||
return m_rptCall1;
|
||||
}
|
||||
|
||||
wxString CHeaderData::getRptCall2() const
|
||||
{
|
||||
return m_rptCall2;
|
||||
}
|
||||
|
||||
unsigned char CHeaderData::getFlag1() const
|
||||
{
|
||||
return m_flag1;
|
||||
}
|
||||
|
||||
unsigned char CHeaderData::getFlag2() const
|
||||
{
|
||||
return m_flag2;
|
||||
}
|
||||
|
||||
unsigned char CHeaderData::getFlag3() const
|
||||
{
|
||||
return m_flag3;
|
||||
}
|
||||
|
||||
void CHeaderData::setMyCall1(const wxString& callsign)
|
||||
{
|
||||
m_myCall1 = callsign;
|
||||
m_myCall1.Append(wxT(' '), LONG_CALLSIGN_LENGTH);
|
||||
m_myCall1.Truncate(LONG_CALLSIGN_LENGTH);
|
||||
}
|
||||
|
||||
void CHeaderData::setMyCall2(const wxString& callsign)
|
||||
{
|
||||
m_myCall2 = callsign;
|
||||
m_myCall2.Append(wxT(' '), SHORT_CALLSIGN_LENGTH);
|
||||
m_myCall2.Truncate(SHORT_CALLSIGN_LENGTH);
|
||||
}
|
||||
|
||||
void CHeaderData::setYourCall(const wxString& callsign)
|
||||
{
|
||||
m_yourCall = callsign;
|
||||
m_yourCall.Append(wxT(' '), LONG_CALLSIGN_LENGTH);
|
||||
m_yourCall.Truncate(LONG_CALLSIGN_LENGTH);
|
||||
}
|
||||
|
||||
void CHeaderData::setRptCall1(const wxString& callsign)
|
||||
{
|
||||
m_rptCall1 = callsign;
|
||||
m_rptCall1.Append(wxT(' '), LONG_CALLSIGN_LENGTH);
|
||||
m_rptCall1.Truncate(LONG_CALLSIGN_LENGTH);
|
||||
}
|
||||
|
||||
void CHeaderData::setRptCall2(const wxString& callsign)
|
||||
{
|
||||
m_rptCall2 = callsign;
|
||||
m_rptCall2.Append(wxT(' '), LONG_CALLSIGN_LENGTH);
|
||||
m_rptCall2.Truncate(LONG_CALLSIGN_LENGTH);
|
||||
}
|
||||
|
||||
bool CHeaderData::isAck() const
|
||||
{
|
||||
return (m_flag1 & ACK_FLAG) == ACK_FLAG;
|
||||
}
|
||||
|
||||
bool CHeaderData::isNoResponse() const
|
||||
{
|
||||
return (m_flag1 & NO_RESPONSE) == NO_RESPONSE;
|
||||
}
|
||||
|
||||
bool CHeaderData::isRelayUnavailable() const
|
||||
{
|
||||
return (m_flag1 & RELAY_UNAVAILABLE) == RELAY_UNAVAILABLE;
|
||||
}
|
||||
|
||||
void CHeaderData::setRepeaterMode(bool set)
|
||||
{
|
||||
if (set)
|
||||
m_flag1 |= REPEATER_MASK;
|
||||
else
|
||||
m_flag1 &= ~REPEATER_MASK;
|
||||
}
|
||||
|
||||
bool CHeaderData::isRepeaterMode() const
|
||||
{
|
||||
return (m_flag1 & REPEATER_MASK) == REPEATER_MASK;
|
||||
}
|
||||
|
||||
void CHeaderData::setDataPacket(bool set)
|
||||
{
|
||||
if (set)
|
||||
m_flag1 |= DATA_MASK;
|
||||
else
|
||||
m_flag1 &= ~DATA_MASK;
|
||||
}
|
||||
|
||||
bool CHeaderData::isDataPacket() const
|
||||
{
|
||||
return (m_flag1 & DATA_MASK) == DATA_MASK;
|
||||
}
|
||||
|
||||
void CHeaderData::setInterrupted(bool set)
|
||||
{
|
||||
if (set)
|
||||
m_flag1 |= INTERRUPTED_MASK;
|
||||
else
|
||||
m_flag1 &= ~INTERRUPTED_MASK;
|
||||
}
|
||||
|
||||
bool CHeaderData::isInterrupted() const
|
||||
{
|
||||
return (m_flag1 & INTERRUPTED_MASK) == INTERRUPTED_MASK;
|
||||
}
|
||||
|
||||
void CHeaderData::setControlSignal(bool set)
|
||||
{
|
||||
if (set)
|
||||
m_flag1 |= CONTROL_SIGNAL_MASK;
|
||||
else
|
||||
m_flag1 &= ~CONTROL_SIGNAL_MASK;
|
||||
}
|
||||
|
||||
bool CHeaderData::isControlSignal() const
|
||||
{
|
||||
return (m_flag1 & CONTROL_SIGNAL_MASK) == CONTROL_SIGNAL_MASK;
|
||||
}
|
||||
|
||||
void CHeaderData::setUrgent(bool set)
|
||||
{
|
||||
if (set)
|
||||
m_flag1 |= URGENT_MASK;
|
||||
else
|
||||
m_flag1 &= ~URGENT_MASK;
|
||||
}
|
||||
|
||||
bool CHeaderData::isUrgent() const
|
||||
{
|
||||
return (m_flag1 & URGENT_MASK) == URGENT_MASK;
|
||||
}
|
||||
|
||||
void CHeaderData::setFlag1(unsigned char flag)
|
||||
{
|
||||
m_flag1 = flag;
|
||||
}
|
||||
|
||||
void CHeaderData::setFlag2(unsigned char flag)
|
||||
{
|
||||
m_flag2 = flag;
|
||||
}
|
||||
|
||||
void CHeaderData::setFlag3(unsigned char flag)
|
||||
{
|
||||
m_flag3 = flag;
|
||||
}
|
||||
|
||||
void CHeaderData::setRepeaterFlags(unsigned char set)
|
||||
{
|
||||
m_flag1 &= ~REPEATER_CONTROL_MASK;
|
||||
m_flag1 |= set & REPEATER_CONTROL_MASK;
|
||||
}
|
||||
|
||||
unsigned char CHeaderData::getRepeaterFlags() const
|
||||
{
|
||||
return m_flag1 & REPEATER_CONTROL_MASK;
|
||||
}
|
||||
|
||||
bool CHeaderData::isValid() const
|
||||
{
|
||||
return m_valid;
|
||||
}
|
||||
|
||||
void CHeaderData::reset()
|
||||
{
|
||||
m_myCall1 = wxT(" ");
|
||||
m_myCall2 = wxT(" ");
|
||||
m_yourCall = wxT("CQCQCQ ");
|
||||
m_rptCall1 = wxT("DIRECT ");
|
||||
m_rptCall2 = wxT("DIRECT ");
|
||||
|
||||
m_flag1 = 0x00;
|
||||
m_flag2 = 0x00;
|
||||
m_flag3 = 0x00;
|
||||
}
|
||||
|
||||
CHeaderData& CHeaderData::operator=(const CHeaderData& header)
|
||||
{
|
||||
if (&header != this) {
|
||||
m_time = header.m_time;
|
||||
m_myCall1 = header.m_myCall1;
|
||||
m_myCall2 = header.m_myCall2;
|
||||
m_yourCall = header.m_yourCall;
|
||||
m_rptCall1 = header.m_rptCall1;
|
||||
m_rptCall2 = header.m_rptCall2;
|
||||
m_flag1 = header.m_flag1;
|
||||
m_flag2 = header.m_flag2;
|
||||
m_flag3 = header.m_flag3;
|
||||
m_valid = header.m_valid;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (C) 2009,2013 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef HeaderData_H
|
||||
#define HeaderData_H
|
||||
|
||||
#include <wx/wx.h>
|
||||
#include <wx/datetime.h>
|
||||
|
||||
class CHeaderData {
|
||||
public:
|
||||
CHeaderData();
|
||||
CHeaderData(const CHeaderData& header);
|
||||
CHeaderData(const unsigned char* data, unsigned int length, bool check);
|
||||
CHeaderData(const wxString& myCall1, const wxString& myCall2, const wxString& yourCall,
|
||||
const wxString& rptCall1, const wxString& rptCall2, unsigned char flag1 = 0x00,
|
||||
unsigned char flag2 = 0x00, unsigned char flag3 = 0x00);
|
||||
~CHeaderData();
|
||||
|
||||
wxDateTime getTime() const;
|
||||
wxString getMyCall1() const;
|
||||
wxString getMyCall2() const;
|
||||
wxString getYourCall() const;
|
||||
wxString getRptCall1() const;
|
||||
wxString getRptCall2() const;
|
||||
|
||||
unsigned char getFlag1() const;
|
||||
unsigned char getFlag2() const;
|
||||
unsigned char getFlag3() const;
|
||||
|
||||
bool isAck() const;
|
||||
bool isNoResponse() const;
|
||||
bool isRelayUnavailable() const;
|
||||
bool isRepeaterMode() const;
|
||||
bool isDataPacket() const;
|
||||
bool isInterrupted() const;
|
||||
bool isControlSignal() const;
|
||||
bool isUrgent() const;
|
||||
unsigned char getRepeaterFlags() const;
|
||||
|
||||
void setFlag1(unsigned char flag);
|
||||
void setFlag2(unsigned char flag);
|
||||
void setFlag3(unsigned char flag);
|
||||
|
||||
void setMyCall1(const wxString& callsign);
|
||||
void setMyCall2(const wxString& callsign);
|
||||
void setYourCall(const wxString& callsign);
|
||||
void setRptCall1(const wxString& callsign);
|
||||
void setRptCall2(const wxString& callsign);
|
||||
|
||||
void setRepeaterMode(bool set);
|
||||
void setDataPacket(bool set);
|
||||
void setInterrupted(bool set);
|
||||
void setControlSignal(bool set);
|
||||
void setUrgent(bool set);
|
||||
void setRepeaterFlags(unsigned char set);
|
||||
|
||||
bool isValid() const;
|
||||
|
||||
void reset();
|
||||
|
||||
CHeaderData& operator=(const CHeaderData& header);
|
||||
|
||||
private:
|
||||
wxDateTime m_time;
|
||||
wxString m_myCall1;
|
||||
wxString m_myCall2;
|
||||
wxString m_yourCall;
|
||||
wxString m_rptCall1;
|
||||
wxString m_rptCall2;
|
||||
unsigned char m_flag1;
|
||||
unsigned char m_flag2;
|
||||
unsigned char m_flag3;
|
||||
bool m_valid;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,412 @@
|
||||
/*
|
||||
* Copyright (C) 2009,2010 by Jonathan Naylor, G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "K8055Controller.h"
|
||||
|
||||
const unsigned int VELLEMAN_VENDOR_ID = 0x10CFU;
|
||||
const unsigned int VELLEMAN_PRODUCT_ID = 0x5500U;
|
||||
|
||||
const char REPORT_ID = 0x00U;
|
||||
|
||||
const char CMD_RESET = 0x00U;
|
||||
const char CMD_SET_ANALOG_DIGITAL = 0x05U;
|
||||
|
||||
const char IN_PORT1 = 0x10U;
|
||||
const char IN_PORT2 = 0x20U;
|
||||
const char IN_PORT3 = 0x01U;
|
||||
const char IN_PORT4 = 0x40U;
|
||||
const char IN_PORT5 = 0x80U;
|
||||
|
||||
const char OUT_PORT1 = 0x01U;
|
||||
const char OUT_PORT2 = 0x02U;
|
||||
const char OUT_PORT3 = 0x04U;
|
||||
const char OUT_PORT4 = 0x08U;
|
||||
const char OUT_PORT5 = 0x10U;
|
||||
const char OUT_PORT6 = 0x20U;
|
||||
const char OUT_PORT7 = 0x40U;
|
||||
const char OUT_PORT8 = 0x80U;
|
||||
|
||||
#if defined(__WINDOWS__)
|
||||
|
||||
#include <Setupapi.h>
|
||||
#include <hidsdi.h>
|
||||
|
||||
const DWORD USB_BUFSIZE = 9UL;
|
||||
|
||||
CK8055Controller::CK8055Controller(unsigned int address) :
|
||||
m_address(address),
|
||||
m_outp1(false),
|
||||
m_outp2(false),
|
||||
m_outp3(false),
|
||||
m_outp4(false),
|
||||
m_outp5(false),
|
||||
m_outp6(false),
|
||||
m_outp7(false),
|
||||
m_outp8(false),
|
||||
m_handle(INVALID_HANDLE_VALUE)
|
||||
{
|
||||
}
|
||||
|
||||
CK8055Controller::~CK8055Controller()
|
||||
{
|
||||
}
|
||||
|
||||
bool CK8055Controller::open()
|
||||
{
|
||||
wxASSERT(m_handle == INVALID_HANDLE_VALUE);
|
||||
|
||||
GUID guid;
|
||||
::HidD_GetHidGuid(&guid);
|
||||
|
||||
HDEVINFO devInfo = ::SetupDiGetClassDevs(&guid, NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
|
||||
if (devInfo == INVALID_HANDLE_VALUE) {
|
||||
wxLogError(wxT("Error from SetupDiGetClassDevs: err=%u"), ::GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
SP_DEVICE_INTERFACE_DATA devInfoData;
|
||||
devInfoData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
|
||||
|
||||
for (DWORD index = 0U; ::SetupDiEnumDeviceInterfaces(devInfo, NULL, &guid, index, &devInfoData); index++) {
|
||||
// Find the required length of the device structure
|
||||
DWORD length;
|
||||
::SetupDiGetDeviceInterfaceDetail(devInfo, &devInfoData, NULL, 0U, &length, NULL);
|
||||
|
||||
PSP_DEVICE_INTERFACE_DETAIL_DATA detailData = PSP_DEVICE_INTERFACE_DETAIL_DATA(::malloc(length));
|
||||
detailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
|
||||
|
||||
// Get the detailed data into the newly allocated device structure
|
||||
DWORD required;
|
||||
BOOL res = ::SetupDiGetDeviceInterfaceDetail(devInfo, &devInfoData, detailData, length, &required, NULL);
|
||||
if (!res) {
|
||||
wxLogError(wxT("Error from SetupDiGetDeviceInterfaceDetail: err=%u"), ::GetLastError());
|
||||
::SetupDiDestroyDeviceInfoList(devInfo);
|
||||
::free(detailData);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the handle for getting the attributes
|
||||
HANDLE handle = ::CreateFile(detailData->DevicePath, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
|
||||
if (handle == INVALID_HANDLE_VALUE) {
|
||||
wxLogError(wxT("Error from CreateFile: err=%u"), ::GetLastError());
|
||||
::SetupDiDestroyDeviceInfoList(devInfo);
|
||||
::free(detailData);
|
||||
return false;
|
||||
}
|
||||
|
||||
HIDD_ATTRIBUTES attributes;
|
||||
attributes.Size = sizeof(HIDD_ATTRIBUTES);
|
||||
res = ::HidD_GetAttributes(handle, &attributes);
|
||||
if (!res) {
|
||||
wxLogError(wxT("Error from HidD_GetAttributes: err=%u"), ::GetLastError());
|
||||
::CloseHandle(handle);
|
||||
::SetupDiDestroyDeviceInfoList(devInfo);
|
||||
::free(detailData);
|
||||
return false;
|
||||
}
|
||||
|
||||
::CloseHandle(handle);
|
||||
|
||||
// Is this a Velleman K8055 and the right one?
|
||||
if (attributes.VendorID == VELLEMAN_VENDOR_ID && attributes.ProductID == (VELLEMAN_PRODUCT_ID + m_address)) {
|
||||
m_handle = ::CreateFile(detailData->DevicePath, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
|
||||
if (m_handle == INVALID_HANDLE_VALUE) {
|
||||
wxLogError(wxT("Error from CreateFile: err=%u"), ::GetLastError());
|
||||
::SetupDiDestroyDeviceInfoList(devInfo);
|
||||
::free(detailData);
|
||||
return false;
|
||||
}
|
||||
|
||||
::SetupDiDestroyDeviceInfoList(devInfo);
|
||||
::free(detailData);
|
||||
|
||||
setDigitalOutputs(false, false, false, false, false, false, false, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
::free(detailData);
|
||||
}
|
||||
|
||||
::SetupDiDestroyDeviceInfoList(devInfo);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CK8055Controller::getDigitalInputs(bool& inp1, bool& inp2, bool& inp3, bool& inp4, bool& inp5)
|
||||
{
|
||||
wxASSERT(m_handle != INVALID_HANDLE_VALUE);
|
||||
|
||||
char buffer[USB_BUFSIZE];
|
||||
buffer[0] = REPORT_ID;
|
||||
buffer[1] = 0x00U;
|
||||
buffer[2] = 0x00U;
|
||||
buffer[3] = 0x00U;
|
||||
buffer[4] = 0x00U;
|
||||
buffer[5] = 0x00U;
|
||||
buffer[6] = 0x00U;
|
||||
buffer[7] = 0x00U;
|
||||
buffer[8] = 0x00U;
|
||||
|
||||
DWORD read;
|
||||
BOOL res = ::ReadFile(m_handle, buffer, USB_BUFSIZE, &read, NULL);
|
||||
if (!res) {
|
||||
wxLogError(wxT("Error from ReadFile: err=%u, read=%u"), ::GetLastError(), read);
|
||||
return;
|
||||
}
|
||||
|
||||
// Do we have data?
|
||||
if (buffer[2] != 0x00) {
|
||||
inp1 = (buffer[1] & IN_PORT1) == IN_PORT1;
|
||||
inp2 = (buffer[1] & IN_PORT2) == IN_PORT2;
|
||||
inp3 = (buffer[1] & IN_PORT3) == IN_PORT3;
|
||||
inp4 = (buffer[1] & IN_PORT4) == IN_PORT4;
|
||||
inp5 = (buffer[1] & IN_PORT5) == IN_PORT5;
|
||||
}
|
||||
}
|
||||
|
||||
void CK8055Controller::setDigitalOutputs(bool outp1, bool outp2, bool outp3, bool outp4, bool outp5, bool outp6, bool outp7, bool outp8)
|
||||
{
|
||||
wxASSERT(m_handle != INVALID_HANDLE_VALUE);
|
||||
|
||||
if (outp1 == m_outp1 && outp2 == m_outp2 && outp3 == m_outp3 && outp4 == m_outp4 &&
|
||||
outp5 == m_outp5 && outp6 == m_outp6 && outp7 == m_outp7 && outp8 == m_outp8)
|
||||
return;
|
||||
|
||||
char buffer[USB_BUFSIZE];
|
||||
buffer[0] = REPORT_ID;
|
||||
buffer[1] = CMD_SET_ANALOG_DIGITAL;
|
||||
buffer[2] = 0x00U;
|
||||
buffer[3] = 0x00U;
|
||||
buffer[4] = 0x00U;
|
||||
buffer[5] = 0x00U;
|
||||
buffer[6] = 0x00U;
|
||||
buffer[7] = 0x00U;
|
||||
buffer[8] = 0x00U;
|
||||
|
||||
if (outp1)
|
||||
buffer[2] |= OUT_PORT1;
|
||||
if (outp2)
|
||||
buffer[2] |= OUT_PORT2;
|
||||
if (outp3)
|
||||
buffer[2] |= OUT_PORT3;
|
||||
if (outp4)
|
||||
buffer[2] |= OUT_PORT4;
|
||||
if (outp5)
|
||||
buffer[2] |= OUT_PORT5;
|
||||
if (outp6)
|
||||
buffer[2] |= OUT_PORT6;
|
||||
if (outp7)
|
||||
buffer[2] |= OUT_PORT7;
|
||||
if (outp8)
|
||||
buffer[2] |= OUT_PORT8;
|
||||
|
||||
DWORD written;
|
||||
BOOL res = ::WriteFile(m_handle, buffer, USB_BUFSIZE, &written, NULL);
|
||||
if (!res || written != USB_BUFSIZE)
|
||||
wxLogError(wxT("Error from WriteFile: err=%u, written=%u"), ::GetLastError(), written);
|
||||
|
||||
m_outp1 = outp1;
|
||||
m_outp2 = outp2;
|
||||
m_outp3 = outp3;
|
||||
m_outp4 = outp4;
|
||||
m_outp5 = outp5;
|
||||
m_outp6 = outp6;
|
||||
m_outp7 = outp7;
|
||||
m_outp8 = outp8;
|
||||
}
|
||||
|
||||
void CK8055Controller::close()
|
||||
{
|
||||
wxASSERT(m_handle != INVALID_HANDLE_VALUE);
|
||||
|
||||
setDigitalOutputs(false, false, false, false, false, false, false, false);
|
||||
|
||||
::CloseHandle(m_handle);
|
||||
m_handle = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
const unsigned int VELLEMAN_HID_INTERFACE = 0U;
|
||||
|
||||
const unsigned int USB_OUTPUT_ENDPOINT = 0x01U;
|
||||
const unsigned int USB_INPUT_ENDPOINT = 0x81U;
|
||||
|
||||
const int USB_BUFSIZE = 8;
|
||||
|
||||
const int USB_TIMEOUT = 5000;
|
||||
|
||||
CK8055Controller::CK8055Controller(unsigned int address) :
|
||||
m_address(address),
|
||||
m_outp1(false),
|
||||
m_outp2(false),
|
||||
m_outp3(false),
|
||||
m_outp4(false),
|
||||
m_outp5(false),
|
||||
m_outp6(false),
|
||||
m_outp7(false),
|
||||
m_outp8(false),
|
||||
m_context(NULL),
|
||||
m_handle(NULL)
|
||||
{
|
||||
::libusb_init(&m_context);
|
||||
}
|
||||
|
||||
CK8055Controller::~CK8055Controller()
|
||||
{
|
||||
wxASSERT(m_context != NULL);
|
||||
|
||||
::libusb_exit(m_context);
|
||||
}
|
||||
|
||||
bool CK8055Controller::open()
|
||||
{
|
||||
wxASSERT(m_context != NULL);
|
||||
wxASSERT(m_handle == NULL);
|
||||
|
||||
m_handle = ::libusb_open_device_with_vid_pid(m_context, VELLEMAN_VENDOR_ID, VELLEMAN_PRODUCT_ID + m_address);
|
||||
if (m_handle == NULL) {
|
||||
wxLogError(wxT("Could not open the Velleman K8055"));
|
||||
return false;
|
||||
}
|
||||
|
||||
int res = ::libusb_claim_interface(m_handle, VELLEMAN_HID_INTERFACE);
|
||||
if (res != 0) {
|
||||
res = ::libusb_detach_kernel_driver(m_handle, VELLEMAN_HID_INTERFACE);
|
||||
if (res != 0) {
|
||||
wxLogError(wxT("Error from libusb_detach_kernel_driver: err=%d"), res);
|
||||
::libusb_close(m_handle);
|
||||
m_handle = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
res = ::libusb_claim_interface(m_handle, VELLEMAN_HID_INTERFACE);
|
||||
if (res != 0) {
|
||||
wxLogError(wxT("Error from libusb_claim_interface: err=%d"), res);
|
||||
::libusb_close(m_handle);
|
||||
m_handle = NULL;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
::libusb_set_configuration(m_handle, 1);
|
||||
|
||||
setDigitalOutputs(false, false, false, false, false, false, false, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CK8055Controller::getDigitalInputs(bool& inp1, bool& inp2, bool& inp3, bool& inp4, bool& inp5)
|
||||
{
|
||||
wxASSERT(m_handle != NULL);
|
||||
|
||||
unsigned char buffer[USB_BUFSIZE];
|
||||
buffer[0] = 0x00;
|
||||
buffer[1] = 0x00;
|
||||
buffer[2] = 0x00;
|
||||
buffer[3] = 0x00;
|
||||
buffer[4] = 0x00;
|
||||
buffer[5] = 0x00;
|
||||
buffer[6] = 0x00;
|
||||
buffer[7] = 0x00;
|
||||
|
||||
int written;
|
||||
int res = ::libusb_interrupt_transfer(m_handle, USB_INPUT_ENDPOINT, buffer, USB_BUFSIZE, &written, USB_TIMEOUT);
|
||||
if (res != 0) {
|
||||
wxLogError(wxT("Error from libusb_interrupt_transfer: err=%d"), res);
|
||||
return;
|
||||
}
|
||||
|
||||
if (written != USB_BUFSIZE)
|
||||
return;
|
||||
|
||||
// Do we have data?
|
||||
if (buffer[1] != 0x00) {
|
||||
inp1 = (buffer[0] & IN_PORT1) == IN_PORT1;
|
||||
inp2 = (buffer[0] & IN_PORT2) == IN_PORT2;
|
||||
inp3 = (buffer[0] & IN_PORT3) == IN_PORT3;
|
||||
inp4 = (buffer[0] & IN_PORT4) == IN_PORT4;
|
||||
inp5 = (buffer[0] & IN_PORT5) == IN_PORT5;
|
||||
}
|
||||
}
|
||||
|
||||
void CK8055Controller::setDigitalOutputs(bool outp1, bool outp2, bool outp3, bool outp4, bool outp5, bool outp6, bool outp7, bool outp8)
|
||||
{
|
||||
wxASSERT(m_handle != NULL);
|
||||
|
||||
if (outp1 == m_outp1 && outp2 == m_outp2 && outp3 == m_outp3 && outp4 == m_outp4 &&
|
||||
outp5 == m_outp5 && outp6 == m_outp6 && outp7 == m_outp7 && outp8 == m_outp8)
|
||||
return;
|
||||
|
||||
unsigned char buffer[USB_BUFSIZE];
|
||||
buffer[0] = CMD_SET_ANALOG_DIGITAL;
|
||||
buffer[1] = 0x00;
|
||||
buffer[2] = 0x00;
|
||||
buffer[3] = 0x00;
|
||||
buffer[4] = 0x00;
|
||||
buffer[5] = 0x00;
|
||||
buffer[6] = 0x00;
|
||||
buffer[7] = 0x00;
|
||||
|
||||
if (outp1)
|
||||
buffer[1] |= OUT_PORT1;
|
||||
if (outp2)
|
||||
buffer[1] |= OUT_PORT2;
|
||||
if (outp3)
|
||||
buffer[1] |= OUT_PORT3;
|
||||
if (outp4)
|
||||
buffer[1] |= OUT_PORT4;
|
||||
if (outp5)
|
||||
buffer[1] |= OUT_PORT5;
|
||||
if (outp6)
|
||||
buffer[1] |= OUT_PORT6;
|
||||
if (outp7)
|
||||
buffer[1] |= OUT_PORT7;
|
||||
if (outp8)
|
||||
buffer[1] |= OUT_PORT8;
|
||||
|
||||
int written;
|
||||
int res = ::libusb_interrupt_transfer(m_handle, USB_OUTPUT_ENDPOINT, buffer, USB_BUFSIZE, &written, USB_TIMEOUT);
|
||||
if (res != 0) {
|
||||
wxLogError(wxT("Error from libusb_interrupt_transfer: err=%d"), res);
|
||||
return;
|
||||
}
|
||||
|
||||
if (written != USB_BUFSIZE)
|
||||
return;
|
||||
|
||||
m_outp1 = outp1;
|
||||
m_outp2 = outp2;
|
||||
m_outp3 = outp3;
|
||||
m_outp4 = outp4;
|
||||
m_outp5 = outp5;
|
||||
m_outp6 = outp6;
|
||||
m_outp7 = outp7;
|
||||
m_outp8 = outp8;
|
||||
}
|
||||
|
||||
void CK8055Controller::close()
|
||||
{
|
||||
wxASSERT(m_handle != NULL);
|
||||
|
||||
setDigitalOutputs(false, false, false, false, false, false, false, false);
|
||||
|
||||
::libusb_release_interface(m_handle, VELLEMAN_HID_INTERFACE);
|
||||
|
||||
::libusb_close(m_handle);
|
||||
m_handle = NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (C) 2009 by Jonathan Naylor, G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef K8055Controller_H
|
||||
#define K8055Controller_H
|
||||
|
||||
#include "HardwareController.h"
|
||||
|
||||
#include <wx/wx.h>
|
||||
|
||||
#if defined(__WINDOWS__)
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <libusb-1.0/libusb.h>
|
||||
#endif
|
||||
|
||||
class CK8055Controller : public IHardwareController {
|
||||
public:
|
||||
CK8055Controller(unsigned int address);
|
||||
virtual ~CK8055Controller();
|
||||
|
||||
virtual bool open();
|
||||
|
||||
virtual void getDigitalInputs(bool& inp1, bool& inp2, bool& inp3, bool& inp4, bool& inp5);
|
||||
|
||||
virtual void setDigitalOutputs(bool outp1, bool outp2, bool outp3, bool outp4, bool outp5, bool outp6, bool outp7, bool outp8);
|
||||
|
||||
virtual void close();
|
||||
|
||||
private:
|
||||
unsigned int m_address;
|
||||
bool m_outp1;
|
||||
bool m_outp2;
|
||||
bool m_outp3;
|
||||
bool m_outp4;
|
||||
bool m_outp5;
|
||||
bool m_outp6;
|
||||
bool m_outp7;
|
||||
bool m_outp8;
|
||||
#if defined(__WINDOWS__)
|
||||
HANDLE m_handle;
|
||||
#else
|
||||
libusb_context* m_context;
|
||||
libusb_device_handle* m_handle;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (C) 2010 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "LogEvent.h"
|
||||
|
||||
CLogEvent::CLogEvent(const wxString& text, wxEventType type, int id) :
|
||||
wxEvent(id, type),
|
||||
m_text(text)
|
||||
{
|
||||
}
|
||||
|
||||
CLogEvent::CLogEvent(const CLogEvent& event) :
|
||||
wxEvent(event),
|
||||
m_text(event.m_text)
|
||||
{
|
||||
}
|
||||
|
||||
CLogEvent::~CLogEvent()
|
||||
{
|
||||
}
|
||||
|
||||
wxString CLogEvent::getText() const
|
||||
{
|
||||
return m_text;
|
||||
}
|
||||
|
||||
wxEvent* CLogEvent::Clone() const
|
||||
{
|
||||
return new CLogEvent(*this);
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (C) 2010 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef LogEvent_H
|
||||
#define LogEvent_H
|
||||
|
||||
#include <wx/wx.h>
|
||||
|
||||
class CLogEvent : public wxEvent {
|
||||
public:
|
||||
CLogEvent(const wxString& text, wxEventType type, int id = 0);
|
||||
virtual ~CLogEvent();
|
||||
|
||||
virtual wxString getText() const;
|
||||
|
||||
virtual wxEvent* Clone() const;
|
||||
|
||||
protected:
|
||||
CLogEvent(const CLogEvent& event);
|
||||
|
||||
private:
|
||||
wxString m_text;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright (C) 2002,2003,2009,2011,2012,2018 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "Logger.h"
|
||||
|
||||
CLogger::CLogger(const wxString& directory, const wxString& name) :
|
||||
#if(defined(__WINDOWS__))
|
||||
m_day(0),
|
||||
#endif
|
||||
wxLog(),
|
||||
m_name(name),
|
||||
m_file(NULL),
|
||||
m_fileName()
|
||||
{
|
||||
m_file = new wxFFile;
|
||||
|
||||
m_fileName.SetPath(directory);
|
||||
m_fileName.SetExt(wxT("log"));
|
||||
|
||||
#if(defined(__WINDOWS__))
|
||||
time_t timestamp;
|
||||
::time(×tamp);
|
||||
struct tm* tm = ::gmtime(×tamp);
|
||||
|
||||
wxString text;
|
||||
text.Printf(wxT("%s-%04d-%02d-%02d"), m_name.c_str(), tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday);
|
||||
|
||||
m_day = tm->tm_yday;
|
||||
m_fileName.SetName(text);
|
||||
#else
|
||||
m_fileName.SetName(m_name);
|
||||
#endif
|
||||
|
||||
bool ret = m_file->Open(m_fileName.GetFullPath(), wxT("a+t"));
|
||||
if (!ret) {
|
||||
wxLogError(wxT("Cannot open %s file for appending"), m_fileName.GetFullPath().c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
CLogger::~CLogger()
|
||||
{
|
||||
wxASSERT(m_file != NULL);
|
||||
|
||||
m_file->Close();
|
||||
delete m_file;
|
||||
}
|
||||
|
||||
void CLogger::DoLogRecord(wxLogLevel level, const wxString& msg, const wxLogRecordInfo& info)
|
||||
{
|
||||
wxASSERT(m_file != NULL);
|
||||
wxASSERT(m_file->IsOpened());
|
||||
|
||||
wxString letter;
|
||||
|
||||
switch (level) {
|
||||
case wxLOG_FatalError: letter = wxT("F"); break;
|
||||
case wxLOG_Error: letter = wxT("E"); break;
|
||||
case wxLOG_Warning: letter = wxT("W"); break;
|
||||
case wxLOG_Info: letter = wxT("I"); break;
|
||||
case wxLOG_Message: letter = wxT("M"); break;
|
||||
case wxLOG_Status: letter = wxT("M"); break;
|
||||
case wxLOG_Trace: letter = wxT("T"); break;
|
||||
case wxLOG_Debug: letter = wxT("D"); break;
|
||||
default: letter = wxT("U"); break;
|
||||
}
|
||||
|
||||
struct tm* tm = ::gmtime(&info.timestamp);
|
||||
|
||||
wxString message;
|
||||
message.Printf(wxT("%s: %04d-%02d-%02d %02d:%02d:%02d: %s\n"), letter.c_str(), tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, msg.c_str());
|
||||
|
||||
logString(message, info.timestamp);
|
||||
|
||||
if (level == wxLOG_FatalError)
|
||||
::abort();
|
||||
}
|
||||
|
||||
void CLogger::logString(const wxString& msg, time_t timestamp)
|
||||
{
|
||||
wxASSERT(m_file != NULL);
|
||||
wxASSERT(m_file->IsOpened());
|
||||
|
||||
#if(defined(__WINDOWS__))
|
||||
struct tm* tm = ::gmtime(×tamp);
|
||||
|
||||
int day = tm->tm_yday;
|
||||
if (day != m_day) {
|
||||
wxString text;
|
||||
text.Printf(wxT("%s-%04d-%02d-%02d"), m_name.c_str(), tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday);
|
||||
|
||||
m_day = day;
|
||||
m_fileName.SetName(text);
|
||||
|
||||
m_file->Close();
|
||||
|
||||
bool ret = m_file->Open(m_fileName.GetFullPath(), wxT("a+t"));
|
||||
if (!ret) {
|
||||
wxLogError(wxT("Cannot open %s file for appending"), m_fileName.GetFullPath().c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
m_file->Write(msg);
|
||||
m_file->Flush();
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (C) 2002,2003,2009,2011,2012,2018 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef Logger_H
|
||||
#define Logger_H
|
||||
|
||||
#include <wx/wx.h>
|
||||
#include <wx/ffile.h>
|
||||
#include <wx/filename.h>
|
||||
#include <wx/log.h>
|
||||
|
||||
class CLogger : public wxLog {
|
||||
public:
|
||||
CLogger(const wxString& directory, const wxString& name);
|
||||
virtual ~CLogger();
|
||||
|
||||
virtual void DoLogRecord(wxLogLevel level, const wxString& msg, const wxLogRecordInfo& info);
|
||||
|
||||
private:
|
||||
wxString m_name;
|
||||
wxFFile* m_file;
|
||||
wxFileName m_fileName;
|
||||
#if(defined(__WINDOWS__))
|
||||
int m_day;
|
||||
#endif
|
||||
|
||||
void logString(const wxString& msg, time_t timestamp);
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,866 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2016 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "CCITTChecksumReverse.h"
|
||||
#include "MMDVMController.h"
|
||||
#include "DStarDefines.h"
|
||||
#include "Timer.h"
|
||||
|
||||
#if defined(__WINDOWS__)
|
||||
#include <setupapi.h>
|
||||
#else
|
||||
#include <wx/dir.h>
|
||||
#endif
|
||||
|
||||
const unsigned char MMDVM_FRAME_START = 0xE0U;
|
||||
|
||||
const unsigned char MMDVM_GET_VERSION = 0x00U;
|
||||
const unsigned char MMDVM_GET_STATUS = 0x01U;
|
||||
const unsigned char MMDVM_SET_CONFIG = 0x02U;
|
||||
|
||||
const unsigned char MMDVM_DSTAR_HEADER = 0x10U;
|
||||
const unsigned char MMDVM_DSTAR_DATA = 0x11U;
|
||||
const unsigned char MMDVM_DSTAR_LOST = 0x12U;
|
||||
const unsigned char MMDVM_DSTAR_EOT = 0x13U;
|
||||
|
||||
const unsigned char MMDVM_ACK = 0x70U;
|
||||
const unsigned char MMDVM_NAK = 0x7FU;
|
||||
|
||||
const unsigned char MMDVM_DUMP = 0xF0U;
|
||||
const unsigned char MMDVM_DEBUG1 = 0xF1U;
|
||||
const unsigned char MMDVM_DEBUG2 = 0xF2U;
|
||||
const unsigned char MMDVM_DEBUG3 = 0xF3U;
|
||||
const unsigned char MMDVM_DEBUG4 = 0xF4U;
|
||||
const unsigned char MMDVM_DEBUG5 = 0xF5U;
|
||||
|
||||
const unsigned int MAX_RESPONSES = 30U;
|
||||
|
||||
const unsigned int BUFFER_LENGTH = 200U;
|
||||
|
||||
CMMDVMController::CMMDVMController(const wxString& port, const wxString& path, bool rxInvert, bool txInvert, bool pttInvert, unsigned int txDelay, unsigned int rxLevel, unsigned int txLevel) :
|
||||
CModem(),
|
||||
m_port(port),
|
||||
m_path(path),
|
||||
m_rxInvert(rxInvert),
|
||||
m_txInvert(txInvert),
|
||||
m_pttInvert(pttInvert),
|
||||
m_txDelay(txDelay),
|
||||
m_txLevel(txLevel),
|
||||
m_rxLevel(rxLevel),
|
||||
m_serial(port, SERIAL_115200, true),
|
||||
m_buffer(NULL),
|
||||
m_txData(1000U),
|
||||
m_rx(false)
|
||||
{
|
||||
wxASSERT(!port.IsEmpty());
|
||||
|
||||
m_buffer = new unsigned char[BUFFER_LENGTH];
|
||||
}
|
||||
|
||||
CMMDVMController::~CMMDVMController()
|
||||
{
|
||||
delete[] m_buffer;
|
||||
}
|
||||
|
||||
bool CMMDVMController::start()
|
||||
{
|
||||
findPort();
|
||||
|
||||
bool ret = openModem();
|
||||
if (!ret)
|
||||
return false;
|
||||
|
||||
findPath();
|
||||
|
||||
Create();
|
||||
SetPriority(100U);
|
||||
Run();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void* CMMDVMController::Entry()
|
||||
{
|
||||
wxLogMessage(wxT("Starting MMDVM Controller thread"));
|
||||
|
||||
// Clock every 5ms-ish
|
||||
CTimer pollTimer(200U, 0U, 100U);
|
||||
pollTimer.start();
|
||||
|
||||
unsigned char writeType = DSMTT_NONE;
|
||||
unsigned char writeLength = 0U;
|
||||
unsigned char* writeBuffer = new unsigned char[BUFFER_LENGTH];
|
||||
|
||||
unsigned int space = 0U;
|
||||
|
||||
while (!m_stopped) {
|
||||
// Poll the modem status every 100ms
|
||||
if (pollTimer.hasExpired()) {
|
||||
bool ret = readStatus();
|
||||
if (!ret) {
|
||||
ret = findModem();
|
||||
if (!ret) {
|
||||
wxLogError(wxT("Stopping MMDVM Controller thread"));
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
pollTimer.start();
|
||||
}
|
||||
|
||||
unsigned int length;
|
||||
RESP_TYPE_MMDVM type = getResponse(m_buffer, length);
|
||||
|
||||
switch (type) {
|
||||
case RTDVM_TIMEOUT:
|
||||
break;
|
||||
|
||||
case RTDVM_ERROR: {
|
||||
bool ret = findModem();
|
||||
if (!ret) {
|
||||
wxLogError(wxT("Stopping MMDVM Controller thread"));
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case RTDVM_DSTAR_HEADER: {
|
||||
// CUtils::dump(wxT("RT_DSTAR_HEADER"), m_buffer, length);
|
||||
wxMutexLocker locker(m_mutex);
|
||||
|
||||
unsigned char data[2U];
|
||||
data[0U] = DSMTT_HEADER;
|
||||
data[1U] = RADIO_HEADER_LENGTH_BYTES;
|
||||
m_rxData.addData(data, 2U);
|
||||
|
||||
m_rxData.addData(m_buffer + 3U, RADIO_HEADER_LENGTH_BYTES);
|
||||
|
||||
m_rx = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case RTDVM_DSTAR_DATA: {
|
||||
// CUtils::dump(wxT("RT_DSTAR_DATA"), m_buffer, length);
|
||||
wxMutexLocker locker(m_mutex);
|
||||
|
||||
unsigned char data[2U];
|
||||
data[0U] = DSMTT_DATA;
|
||||
data[1U] = DV_FRAME_LENGTH_BYTES;
|
||||
m_rxData.addData(data, 2U);
|
||||
|
||||
m_rxData.addData(m_buffer + 3U, DV_FRAME_LENGTH_BYTES);
|
||||
|
||||
m_rx = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case RTDVM_DSTAR_EOT: {
|
||||
// wxLogMessage(wxT("RT_DSTAR_EOT"));
|
||||
wxMutexLocker locker(m_mutex);
|
||||
|
||||
unsigned char data[2U];
|
||||
data[0U] = DSMTT_EOT;
|
||||
data[1U] = 0U;
|
||||
m_rxData.addData(data, 2U);
|
||||
|
||||
m_rx = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case RTDVM_DSTAR_LOST: {
|
||||
// wxLogMessage(wxT("RT_DSTAR_LOST"));
|
||||
wxMutexLocker locker(m_mutex);
|
||||
|
||||
unsigned char data[2U];
|
||||
data[0U] = DSMTT_LOST;
|
||||
data[1U] = 0U;
|
||||
m_rxData.addData(data, 2U);
|
||||
|
||||
m_rx = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case RTDVM_GET_STATUS: {
|
||||
bool dstar = (m_buffer[3U] & 0x01U) == 0x01U;
|
||||
if (!dstar) {
|
||||
wxLogError(wxT("D-Star not enabled in the MMDVM!!!"));
|
||||
wxLogError(wxT("Stopping MMDVM Controller thread"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
m_tx = (m_buffer[5U] & 0x01U) == 0x01U;
|
||||
|
||||
bool adcOverflow = (m_buffer[5U] & 0x02U) == 0x02U;
|
||||
if (adcOverflow)
|
||||
wxLogWarning(wxT("MMDVM ADC levels have overflowed"));
|
||||
|
||||
space = m_buffer[6U];
|
||||
// CUtils::dump(wxT("GET_STATUS"), m_buffer, length);
|
||||
// wxLogMessage(wxT("PTT=%d space=%u"), int(m_tx), space);
|
||||
}
|
||||
break;
|
||||
|
||||
// These should not be received in this loop, but don't complain if we do
|
||||
case RTDVM_GET_VERSION:
|
||||
case RTDVM_ACK:
|
||||
break;
|
||||
|
||||
case RTDVM_NAK: {
|
||||
switch (m_buffer[3U]) {
|
||||
case MMDVM_DSTAR_HEADER:
|
||||
wxLogWarning(wxT("Received a header NAK from the MMDVM, reason = %u"), m_buffer[4U]);
|
||||
break;
|
||||
case MMDVM_DSTAR_DATA:
|
||||
wxLogWarning(wxT("Received a data NAK from the MMDVM, reason = %u"), m_buffer[4U]);
|
||||
break;
|
||||
case MMDVM_DSTAR_EOT:
|
||||
wxLogWarning(wxT("Received an EOT NAK from the MMDVM, reason = %u"), m_buffer[4U]);
|
||||
break;
|
||||
default:
|
||||
wxLogWarning(wxT("Received a NAK from the MMDVM, command = 0x%02X, reason = %u"), m_buffer[3U], m_buffer[4U]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case RTDVM_DUMP:
|
||||
CUtils::dump(wxT("Modem dump"), m_buffer + 3U, length - 3U);
|
||||
break;
|
||||
|
||||
case RTDVM_DEBUG1:
|
||||
case RTDVM_DEBUG2:
|
||||
case RTDVM_DEBUG3:
|
||||
case RTDVM_DEBUG4:
|
||||
case RTDVM_DEBUG5:
|
||||
printDebug();
|
||||
break;
|
||||
|
||||
default:
|
||||
wxLogMessage(wxT("Unknown message, type: %02X"), m_buffer[2U]);
|
||||
CUtils::dump(wxT("Buffer dump"), m_buffer, length);
|
||||
break;
|
||||
}
|
||||
|
||||
if (writeType == DSMTT_NONE && m_txData.hasData()) {
|
||||
wxMutexLocker locker(m_mutex);
|
||||
|
||||
m_txData.getData(&writeType, 1U);
|
||||
m_txData.getData(&writeLength, 1U);
|
||||
m_txData.getData(writeBuffer, writeLength);
|
||||
}
|
||||
|
||||
if (space > 4U && writeType == DSMTT_HEADER) {
|
||||
// CUtils::dump(wxT("Write Header"), writeBuffer, writeLength);
|
||||
|
||||
int ret = m_serial.write(writeBuffer, writeLength);
|
||||
if (ret != int(writeLength))
|
||||
wxLogWarning(wxT("Error when writing the header to the MMDVM"));
|
||||
|
||||
writeType = DSMTT_NONE;
|
||||
space -= 4U;
|
||||
}
|
||||
|
||||
if (space > 1U && (writeType == DSMTT_DATA || writeType == DSMTT_EOT)) {
|
||||
// CUtils::dump(wxT("Write Data"), writeBuffer, writeLength);
|
||||
|
||||
int ret = m_serial.write(writeBuffer, writeLength);
|
||||
if (ret != int(writeLength))
|
||||
wxLogWarning(wxT("Error when writing data to the MMDVM"));
|
||||
|
||||
writeType = DSMTT_NONE;
|
||||
space--;
|
||||
}
|
||||
|
||||
Sleep(5UL);
|
||||
|
||||
pollTimer.clock();
|
||||
}
|
||||
|
||||
wxLogMessage(wxT("Stopping MMDVM Controller thread"));
|
||||
|
||||
delete[] writeBuffer;
|
||||
|
||||
m_serial.close();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool CMMDVMController::writeHeader(const CHeaderData& header)
|
||||
{
|
||||
bool ret = m_txData.hasSpace(46U);
|
||||
if (!ret) {
|
||||
wxLogWarning(wxT("No space to write the header"));
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned char buffer[50U];
|
||||
|
||||
buffer[0U] = MMDVM_FRAME_START;
|
||||
buffer[1U] = RADIO_HEADER_LENGTH_BYTES + 3U;
|
||||
buffer[2U] = MMDVM_DSTAR_HEADER;
|
||||
|
||||
::memset(buffer + 3U, ' ', RADIO_HEADER_LENGTH_BYTES);
|
||||
|
||||
buffer[3U] = header.getFlag1();
|
||||
buffer[4U] = header.getFlag2();
|
||||
buffer[5U] = header.getFlag3();
|
||||
|
||||
wxString rpt2 = header.getRptCall2();
|
||||
for (unsigned int i = 0U; i < rpt2.Len() && i < LONG_CALLSIGN_LENGTH; i++)
|
||||
buffer[i + 6U] = rpt2.GetChar(i);
|
||||
|
||||
wxString rpt1 = header.getRptCall1();
|
||||
for (unsigned int i = 0U; i < rpt1.Len() && i < LONG_CALLSIGN_LENGTH; i++)
|
||||
buffer[i + 14U] = rpt1.GetChar(i);
|
||||
|
||||
wxString your = header.getYourCall();
|
||||
for (unsigned int i = 0U; i < your.Len() && i < LONG_CALLSIGN_LENGTH; i++)
|
||||
buffer[i + 22U] = your.GetChar(i);
|
||||
|
||||
wxString my1 = header.getMyCall1();
|
||||
for (unsigned int i = 0U; i < my1.Len() && i < LONG_CALLSIGN_LENGTH; i++)
|
||||
buffer[i + 30U] = my1.GetChar(i);
|
||||
|
||||
wxString my2 = header.getMyCall2();
|
||||
for (unsigned int i = 0U; i < my2.Len() && i < SHORT_CALLSIGN_LENGTH; i++)
|
||||
buffer[i + 38U] = my2.GetChar(i);
|
||||
|
||||
CCCITTChecksumReverse cksum1;
|
||||
cksum1.update(buffer + 3U, RADIO_HEADER_LENGTH_BYTES - 2U);
|
||||
cksum1.result(buffer + 42U);
|
||||
|
||||
wxMutexLocker locker(m_mutex);
|
||||
|
||||
unsigned char type = DSMTT_HEADER;
|
||||
m_txData.addData(&type, 1U);
|
||||
|
||||
unsigned char len = RADIO_HEADER_LENGTH_BYTES + 3U;
|
||||
m_txData.addData(&len, 1U);
|
||||
|
||||
m_txData.addData(buffer, RADIO_HEADER_LENGTH_BYTES + 3U);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CMMDVMController::writeData(const unsigned char* data, unsigned int length, bool end)
|
||||
{
|
||||
bool ret = m_txData.hasSpace(17U);
|
||||
if (!ret) {
|
||||
wxLogWarning(wxT("No space to write data"));
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned char buffer[20U];
|
||||
|
||||
if (end) {
|
||||
buffer[0U] = MMDVM_FRAME_START;
|
||||
buffer[1U] = 3U;
|
||||
buffer[2U] = MMDVM_DSTAR_EOT;
|
||||
|
||||
wxMutexLocker locker(m_mutex);
|
||||
|
||||
unsigned char type = DSMTT_EOT;
|
||||
m_txData.addData(&type, 1U);
|
||||
|
||||
unsigned char len = 3U;
|
||||
m_txData.addData(&len, 1U);
|
||||
|
||||
m_txData.addData(buffer, 3U);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
buffer[0U] = MMDVM_FRAME_START;
|
||||
buffer[1U] = DV_FRAME_LENGTH_BYTES + 3U;
|
||||
buffer[2U] = MMDVM_DSTAR_DATA;
|
||||
::memcpy(buffer + 3U, data, DV_FRAME_LENGTH_BYTES);
|
||||
|
||||
wxMutexLocker locker(m_mutex);
|
||||
|
||||
unsigned char type = DSMTT_DATA;
|
||||
m_txData.addData(&type, 1U);
|
||||
|
||||
unsigned char len = DV_FRAME_LENGTH_BYTES + 3U;
|
||||
m_txData.addData(&len, 1U);
|
||||
|
||||
m_txData.addData(buffer, DV_FRAME_LENGTH_BYTES + 3U);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned int CMMDVMController::getSpace()
|
||||
{
|
||||
return m_txData.freeSpace() / (DV_FRAME_LENGTH_BYTES + 5U);
|
||||
}
|
||||
|
||||
bool CMMDVMController::isTXReady()
|
||||
{
|
||||
if (m_tx)
|
||||
return false;
|
||||
|
||||
return m_txData.isEmpty();
|
||||
}
|
||||
|
||||
bool CMMDVMController::readVersion()
|
||||
{
|
||||
::wxSleep(2);
|
||||
|
||||
for (unsigned int i = 0U; i < 6U; i++) {
|
||||
unsigned char buffer[3U];
|
||||
|
||||
buffer[0U] = MMDVM_FRAME_START;
|
||||
buffer[1U] = 3U;
|
||||
buffer[2U] = MMDVM_GET_VERSION;
|
||||
|
||||
// CUtils::dump(wxT("Written"), buffer, 3U);
|
||||
|
||||
int ret = m_serial.write(buffer, 3U);
|
||||
if (ret != 3)
|
||||
return false;
|
||||
|
||||
for (unsigned int count = 0U; count < MAX_RESPONSES; count++) {
|
||||
::wxMilliSleep(10UL);
|
||||
|
||||
unsigned int length;
|
||||
RESP_TYPE_MMDVM resp = getResponse(m_buffer, length);
|
||||
if (resp == RTDVM_GET_VERSION) {
|
||||
wxString description((char*)(m_buffer + 4U), wxConvLocal, length - 4U);
|
||||
|
||||
wxLogInfo(wxT("MMDVM protocol version: %u, description: %s"), m_buffer[3U], description.c_str());
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
::wxSleep(1);
|
||||
}
|
||||
|
||||
wxLogError(wxT("Unable to read the firmware version after six attempts"));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CMMDVMController::readStatus()
|
||||
{
|
||||
unsigned char buffer[3U];
|
||||
|
||||
buffer[0U] = MMDVM_FRAME_START;
|
||||
buffer[1U] = 3U;
|
||||
buffer[2U] = MMDVM_GET_STATUS;
|
||||
|
||||
return m_serial.write(buffer, 3U) == 3;
|
||||
}
|
||||
|
||||
bool CMMDVMController::setConfig()
|
||||
{
|
||||
unsigned char buffer[25U];
|
||||
|
||||
buffer[0U] = MMDVM_FRAME_START;
|
||||
|
||||
buffer[1U] = 19U;
|
||||
|
||||
buffer[2U] = MMDVM_SET_CONFIG;
|
||||
|
||||
buffer[3U] = 0x00U;
|
||||
if (m_rxInvert)
|
||||
buffer[3U] |= 0x01U;
|
||||
if (m_txInvert)
|
||||
buffer[3U] |= 0x02U;
|
||||
if (m_pttInvert)
|
||||
buffer[3U] |= 0x04U;
|
||||
|
||||
buffer[4U] = 0x01U; // D-Star only
|
||||
|
||||
buffer[5U] = m_txDelay / 10U; // In 10ms units
|
||||
|
||||
buffer[6U] = 1U; // STATE_DSTAR
|
||||
|
||||
buffer[7U] = (m_rxLevel * 255U) / 100U;
|
||||
buffer[8U] = (m_txLevel * 255U) / 100U;
|
||||
|
||||
buffer[9U] = 0U; // DMR Color Code
|
||||
|
||||
buffer[10U] = 0U; // DMR Delay
|
||||
|
||||
buffer[11U] = 0U; // Osc. Offset
|
||||
|
||||
buffer[12U] = (m_txLevel * 255U) / 100U;
|
||||
buffer[13U] = (m_txLevel * 255U) / 100U;
|
||||
buffer[14U] = (m_txLevel * 255U) / 100U;
|
||||
buffer[15U] = (m_txLevel * 255U) / 100U;
|
||||
|
||||
buffer[16U] = 128U;
|
||||
buffer[17U] = 128U;
|
||||
|
||||
buffer[18U] = (m_txLevel * 255U) / 100U;
|
||||
|
||||
// CUtils::dump(wxT("Written"), buffer, 19U);
|
||||
|
||||
int ret = m_serial.write(buffer, 19U);
|
||||
if (ret != 19U)
|
||||
return false;
|
||||
|
||||
unsigned int count = 0U;
|
||||
unsigned int length;
|
||||
RESP_TYPE_MMDVM resp;
|
||||
do {
|
||||
::wxMilliSleep(10UL);
|
||||
|
||||
resp = getResponse(m_buffer, length);
|
||||
|
||||
if (resp != RTDVM_ACK && resp != RTDVM_NAK) {
|
||||
count++;
|
||||
if (count >= MAX_RESPONSES) {
|
||||
wxLogError(wxT("The MMDVM is not responding to the SET_CONFIG command"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} while (resp != RTDVM_ACK && resp != RTDVM_NAK);
|
||||
|
||||
// CUtils::dump(wxT("Response"), m_buffer, length);
|
||||
|
||||
if (resp == RTDVM_NAK) {
|
||||
wxLogError(wxT("Received a NAK to the SET_CONFIG command from the modem"));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
RESP_TYPE_MMDVM CMMDVMController::getResponse(unsigned char *buffer, unsigned int& length)
|
||||
{
|
||||
// Get the start of the frame or nothing at all
|
||||
int ret = m_serial.read(buffer + 0U, 1U);
|
||||
if (ret < 0) {
|
||||
wxLogError(wxT("Error when reading from the MMDVM"));
|
||||
return RTDVM_ERROR;
|
||||
}
|
||||
|
||||
if (ret == 0)
|
||||
return RTDVM_TIMEOUT;
|
||||
|
||||
if (buffer[0U] != MMDVM_FRAME_START)
|
||||
return RTDVM_TIMEOUT;
|
||||
|
||||
ret = m_serial.read(buffer + 1U, 1U);
|
||||
if (ret < 0) {
|
||||
wxLogError(wxT("Error when reading from the MMDVM"));
|
||||
return RTDVM_ERROR;
|
||||
}
|
||||
|
||||
if (ret == 0)
|
||||
return RTDVM_TIMEOUT;
|
||||
|
||||
length = buffer[1U];
|
||||
|
||||
if (length >= 100U) {
|
||||
wxLogError(wxT("Invalid data received from the MMDVM"));
|
||||
CUtils::dump(wxT("Data"), buffer, 2U);
|
||||
return RTDVM_TIMEOUT;
|
||||
}
|
||||
|
||||
unsigned int offset = 2U;
|
||||
|
||||
while (offset < length) {
|
||||
int ret = m_serial.read(buffer + offset, length - offset);
|
||||
if (ret < 0) {
|
||||
wxLogError(wxT("Error when reading from the MMDVM"));
|
||||
return RTDVM_ERROR;
|
||||
}
|
||||
|
||||
if (ret > 0)
|
||||
offset += ret;
|
||||
|
||||
if (ret == 0)
|
||||
Sleep(5UL);
|
||||
}
|
||||
|
||||
// CUtils::dump(wxT("Received"), buffer, length);
|
||||
|
||||
switch (buffer[2U]) {
|
||||
case MMDVM_GET_STATUS:
|
||||
return RTDVM_GET_STATUS;
|
||||
case MMDVM_GET_VERSION:
|
||||
return RTDVM_GET_VERSION;
|
||||
case MMDVM_DSTAR_HEADER:
|
||||
return RTDVM_DSTAR_HEADER;
|
||||
case MMDVM_DSTAR_DATA:
|
||||
return RTDVM_DSTAR_DATA;
|
||||
case MMDVM_DSTAR_EOT:
|
||||
return RTDVM_DSTAR_EOT;
|
||||
case MMDVM_DSTAR_LOST:
|
||||
return RTDVM_DSTAR_LOST;
|
||||
case MMDVM_ACK:
|
||||
return RTDVM_ACK;
|
||||
case MMDVM_NAK:
|
||||
return RTDVM_NAK;
|
||||
case MMDVM_DUMP:
|
||||
return RTDVM_DUMP;
|
||||
case MMDVM_DEBUG1:
|
||||
return RTDVM_DEBUG1;
|
||||
case MMDVM_DEBUG2:
|
||||
return RTDVM_DEBUG2;
|
||||
case MMDVM_DEBUG3:
|
||||
return RTDVM_DEBUG3;
|
||||
case MMDVM_DEBUG4:
|
||||
return RTDVM_DEBUG4;
|
||||
case MMDVM_DEBUG5:
|
||||
return RTDVM_DEBUG5;
|
||||
default:
|
||||
return RTDVM_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
wxString CMMDVMController::getPath() const
|
||||
{
|
||||
return m_path;
|
||||
}
|
||||
|
||||
bool CMMDVMController::findPort()
|
||||
{
|
||||
if (m_path.IsEmpty())
|
||||
return false;
|
||||
|
||||
#if defined(__WINDOWS__)
|
||||
#else
|
||||
wxDir dir;
|
||||
bool ret1 = dir.Open(wxT("/sys/class/tty"));
|
||||
if (!ret1) {
|
||||
wxLogError(wxT("Cannot open directory /sys/class/tty"));
|
||||
return false;
|
||||
}
|
||||
|
||||
wxString fileName;
|
||||
ret1 = dir.GetFirst(&fileName, wxT("ttyACM*"));
|
||||
while (ret1) {
|
||||
wxString path;
|
||||
path.Printf(wxT("/sys/class/tty/%s"), fileName.c_str());
|
||||
|
||||
char cpath[255U];
|
||||
::memset(cpath, 0x00U, 255U);
|
||||
|
||||
for (unsigned int i = 0U; i < path.Len(); i++)
|
||||
cpath[i] = path.GetChar(i);
|
||||
|
||||
char symlink[255U];
|
||||
int ret2 = ::readlink(cpath, symlink, 255U);
|
||||
if (ret2 < 0) {
|
||||
::strcat(cpath, "/device");
|
||||
ret2 = ::readlink(cpath, symlink, 255U);
|
||||
if (ret2 < 0) {
|
||||
wxLogError(wxT("Error from readlink()"));
|
||||
return false;
|
||||
}
|
||||
|
||||
path = wxString(symlink, wxConvLocal, ret2);
|
||||
} else {
|
||||
// Get all but the last section
|
||||
wxString fullPath = wxString(symlink, wxConvLocal, ret2);
|
||||
path = fullPath.BeforeLast(wxT('/'));
|
||||
}
|
||||
|
||||
if (path.IsSameAs(m_path)) {
|
||||
m_port.Printf(wxT("/dev/%s"), fileName.c_str());
|
||||
|
||||
wxLogMessage(wxT("Found modem port of %s based on the path"), m_port.c_str());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ret1 = dir.GetNext(&fileName);
|
||||
}
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CMMDVMController::findPath()
|
||||
{
|
||||
#if defined(__WINDOWS__)
|
||||
#ifdef notdef
|
||||
GUID guids[5U];
|
||||
|
||||
DWORD count;
|
||||
BOOL res = ::SetupDiClassGuidsFromName(L"Multifunction", guids, 5U, &count);
|
||||
if (!res) {
|
||||
wxLogError(wxT("Error from SetupDiClassGuidsFromName: err=%u"), ::GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
for (DWORD i = 0U; i < count; i++) {
|
||||
HDEVINFO devInfo = ::SetupDiGetClassDevs(&guids[i], NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
|
||||
if (devInfo == INVALID_HANDLE_VALUE) {
|
||||
wxLogError(wxT("Error from SetupDiGetClassDevs: err=%u"), ::GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
SP_DEVICE_INTERFACE_DATA devInfoData;
|
||||
devInfoData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
|
||||
|
||||
for (DWORD index = 0U; ::SetupDiEnumDeviceInterfaces(devInfo, NULL, &guids[i], index, &devInfoData); index++) {
|
||||
// Find the required length of the device structure
|
||||
DWORD length;
|
||||
::SetupDiGetDeviceInterfaceDetail(devInfo, &devInfoData, NULL, 0U, &length, NULL);
|
||||
|
||||
PSP_DEVICE_INTERFACE_DETAIL_DATA detailData = PSP_DEVICE_INTERFACE_DETAIL_DATA(::malloc(length));
|
||||
detailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
|
||||
|
||||
// Get the detailed data into the newly allocated device structure
|
||||
DWORD required;
|
||||
res = ::SetupDiGetDeviceInterfaceDetail(devInfo, &devInfoData, detailData, length, &required, NULL);
|
||||
if (!res) {
|
||||
wxLogError(wxT("Error from SetupDiGetDeviceInterfaceDetail: err=%u"), ::GetLastError());
|
||||
::SetupDiDestroyDeviceInfoList(devInfo);
|
||||
::free(detailData);
|
||||
return false;
|
||||
}
|
||||
|
||||
::free(detailData);
|
||||
}
|
||||
|
||||
::SetupDiDestroyDeviceInfoList(devInfo);
|
||||
}
|
||||
|
||||
return false;
|
||||
#endif
|
||||
#else
|
||||
wxString path;
|
||||
path.Printf(wxT("/sys/class/tty/%s"), m_port.Mid(5U).c_str());
|
||||
|
||||
char cpath[255U];
|
||||
::memset(cpath, 0x00U, 255U);
|
||||
|
||||
for (unsigned int i = 0U; i < path.Len(); i++)
|
||||
cpath[i] = path.GetChar(i);
|
||||
|
||||
char symlink[255U];
|
||||
int ret = ::readlink(cpath, symlink, 255U);
|
||||
if (ret < 0) {
|
||||
::strcat(cpath, "/device");
|
||||
ret = ::readlink(cpath, symlink, 255U);
|
||||
if (ret < 0) {
|
||||
wxLogError(wxT("Error from readlink()"));
|
||||
return false;
|
||||
}
|
||||
|
||||
path = wxString(symlink, wxConvLocal, ret);
|
||||
} else {
|
||||
wxString fullPath = wxString(symlink, wxConvLocal, ret);
|
||||
path = fullPath.BeforeLast(wxT('/'));
|
||||
}
|
||||
|
||||
if (m_path.IsEmpty())
|
||||
wxLogMessage(wxT("Found modem path of %s"), path.c_str());
|
||||
|
||||
m_path = path;
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CMMDVMController::findModem()
|
||||
{
|
||||
m_serial.close();
|
||||
|
||||
// Tell the repeater that the signal has gone away
|
||||
if (m_rx) {
|
||||
wxMutexLocker locker(m_mutex);
|
||||
|
||||
unsigned char data[2U];
|
||||
data[0U] = DSMTT_EOT;
|
||||
data[1U] = 0U;
|
||||
m_rxData.addData(data, 2U);
|
||||
|
||||
m_rx = false;
|
||||
}
|
||||
|
||||
unsigned int count = 0U;
|
||||
|
||||
// Purge the transmit buffer every 500ms to avoid overflow, but only try and reopen the modem every 2s
|
||||
while (!m_stopped) {
|
||||
count++;
|
||||
if (count >= 4U) {
|
||||
wxLogMessage(wxT("Trying to reopen the modem"));
|
||||
|
||||
bool ret = findPort();
|
||||
if (ret) {
|
||||
ret = openModem();
|
||||
if (ret)
|
||||
return true;
|
||||
}
|
||||
|
||||
count = 0U;
|
||||
}
|
||||
|
||||
Sleep(500UL);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CMMDVMController::openModem()
|
||||
{
|
||||
bool ret = m_serial.open();
|
||||
if (!ret)
|
||||
return false;
|
||||
|
||||
ret = readVersion();
|
||||
if (!ret) {
|
||||
m_serial.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = setConfig();
|
||||
if (!ret) {
|
||||
m_serial.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CMMDVMController::printDebug()
|
||||
{
|
||||
unsigned int length = m_buffer[1U];
|
||||
if (m_buffer[2U] == 0xF1U) {
|
||||
wxString message((char*)(m_buffer + 3U), wxConvLocal, length - 3U);
|
||||
wxLogMessage(wxT("Debug: %s"), message.c_str());
|
||||
} else if (m_buffer[2U] == 0xF2U) {
|
||||
wxString message((char*)(m_buffer + 3U), wxConvLocal, length - 5U);
|
||||
short val1 = (m_buffer[length - 2U] << 8) | m_buffer[length - 1U];
|
||||
wxLogMessage(wxT("Debug: %s %d"), message.c_str(), val1);
|
||||
} else if (m_buffer[2U] == 0xF3U) {
|
||||
wxString message((char*)(m_buffer + 3U), wxConvLocal, length - 7U);
|
||||
short val1 = (m_buffer[length - 4U] << 8) | m_buffer[length - 3U];
|
||||
short val2 = (m_buffer[length - 2U] << 8) | m_buffer[length - 1U];
|
||||
wxLogMessage(wxT("Debug: %s %d %d"), message.c_str(), val1, val2);
|
||||
} else if (m_buffer[2U] == 0xF4U) {
|
||||
wxString message((char*)(m_buffer + 3U), wxConvLocal, length - 9U);
|
||||
short val1 = (m_buffer[length - 6U] << 8) | m_buffer[length - 5U];
|
||||
short val2 = (m_buffer[length - 4U] << 8) | m_buffer[length - 3U];
|
||||
short val3 = (m_buffer[length - 2U] << 8) | m_buffer[length - 1U];
|
||||
wxLogMessage(wxT("Debug: %s %d %d %d"), message.c_str(), val1, val2, val3);
|
||||
} else if (m_buffer[2U] == 0xF5U) {
|
||||
wxString message((char*)(m_buffer + 3U), wxConvLocal, length - 11U);
|
||||
short val1 = (m_buffer[length - 8U] << 8) | m_buffer[length - 7U];
|
||||
short val2 = (m_buffer[length - 6U] << 8) | m_buffer[length - 5U];
|
||||
short val3 = (m_buffer[length - 4U] << 8) | m_buffer[length - 3U];
|
||||
short val4 = (m_buffer[length - 2U] << 8) | m_buffer[length - 1U];
|
||||
wxLogMessage(wxT("Debug: %s %d %d %d %d"), message.c_str(), val1, val2, val3, val4);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2015 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef MMDVMController_H
|
||||
#define MMDVMController_H
|
||||
|
||||
#include "SerialDataController.h"
|
||||
#include "RingBuffer.h"
|
||||
#include "Modem.h"
|
||||
#include "Utils.h"
|
||||
|
||||
#include <wx/wx.h>
|
||||
|
||||
enum RESP_TYPE_MMDVM {
|
||||
RTDVM_TIMEOUT,
|
||||
RTDVM_ERROR,
|
||||
RTDVM_UNKNOWN,
|
||||
RTDVM_GET_STATUS,
|
||||
RTDVM_GET_VERSION,
|
||||
RTDVM_DSTAR_HEADER,
|
||||
RTDVM_DSTAR_DATA,
|
||||
RTDVM_DSTAR_EOT,
|
||||
RTDVM_DSTAR_LOST,
|
||||
RTDVM_ACK,
|
||||
RTDVM_NAK,
|
||||
RTDVM_DUMP,
|
||||
RTDVM_DEBUG1,
|
||||
RTDVM_DEBUG2,
|
||||
RTDVM_DEBUG3,
|
||||
RTDVM_DEBUG4,
|
||||
RTDVM_DEBUG5
|
||||
};
|
||||
|
||||
class CMMDVMController : public CModem {
|
||||
public:
|
||||
CMMDVMController(const wxString& port, const wxString& path, bool rxInvert, bool txInvert, bool pttInvert, unsigned int txDelay, unsigned int rxLevel, unsigned int txLevel);
|
||||
virtual ~CMMDVMController();
|
||||
|
||||
virtual void* Entry();
|
||||
|
||||
virtual bool start();
|
||||
|
||||
virtual unsigned int getSpace();
|
||||
virtual bool isTXReady();
|
||||
|
||||
virtual bool writeHeader(const CHeaderData& header);
|
||||
virtual bool writeData(const unsigned char* data, unsigned int length, bool end);
|
||||
|
||||
virtual wxString getPath() const;
|
||||
|
||||
private:
|
||||
wxString m_port;
|
||||
wxString m_path;
|
||||
bool m_rxInvert;
|
||||
bool m_txInvert;
|
||||
bool m_pttInvert;
|
||||
unsigned int m_txDelay;
|
||||
unsigned int m_rxLevel;
|
||||
unsigned int m_txLevel;
|
||||
CSerialDataController m_serial;
|
||||
unsigned char* m_buffer;
|
||||
CRingBuffer<unsigned char> m_txData;
|
||||
bool m_rx;
|
||||
|
||||
bool readVersion();
|
||||
bool readStatus();
|
||||
bool setConfig();
|
||||
|
||||
RESP_TYPE_MMDVM getResponse(unsigned char* buffer, unsigned int& length);
|
||||
|
||||
bool findPort();
|
||||
bool findPath();
|
||||
|
||||
bool findModem();
|
||||
bool openModem();
|
||||
|
||||
void printDebug();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright (C) 2014 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "DStarDefines.h"
|
||||
#include "Modem.h"
|
||||
|
||||
const unsigned int BUFFER_LENGTH = 200U;
|
||||
|
||||
CModem::CModem() :
|
||||
wxThread(wxTHREAD_JOINABLE),
|
||||
m_rxData(1000U),
|
||||
m_mutex(),
|
||||
m_tx(false),
|
||||
m_stopped(false),
|
||||
m_readType(DSMTT_NONE),
|
||||
m_readLength(0U),
|
||||
m_readBuffer(NULL)
|
||||
{
|
||||
m_readBuffer = new unsigned char[BUFFER_LENGTH];
|
||||
}
|
||||
|
||||
CModem::~CModem()
|
||||
{
|
||||
delete[] m_readBuffer;
|
||||
}
|
||||
|
||||
bool CModem::isTX()
|
||||
{
|
||||
return m_tx;
|
||||
}
|
||||
|
||||
DSMT_TYPE CModem::read()
|
||||
{
|
||||
m_readLength = 0U;
|
||||
|
||||
if (m_rxData.isEmpty())
|
||||
return DSMTT_NONE;
|
||||
|
||||
wxMutexLocker locker(m_mutex);
|
||||
|
||||
unsigned char hdr[2U];
|
||||
m_rxData.getData(hdr, 2U);
|
||||
|
||||
m_readType = DSMT_TYPE(hdr[0U]);
|
||||
m_readLength = hdr[1U];
|
||||
m_rxData.getData(m_readBuffer, m_readLength);
|
||||
|
||||
return m_readType;
|
||||
}
|
||||
|
||||
CHeaderData* CModem::readHeader()
|
||||
{
|
||||
if (m_readType != DSMTT_HEADER)
|
||||
return NULL;
|
||||
|
||||
return new CHeaderData(m_readBuffer, RADIO_HEADER_LENGTH_BYTES, false);
|
||||
}
|
||||
|
||||
unsigned int CModem::readData(unsigned char* data, unsigned int length)
|
||||
{
|
||||
if (m_readType != DSMTT_DATA)
|
||||
return 0U;
|
||||
|
||||
if (length < m_readLength) {
|
||||
::memcpy(data, m_readBuffer, length);
|
||||
return length;
|
||||
} else {
|
||||
::memcpy(data, m_readBuffer, m_readLength);
|
||||
return m_readLength;
|
||||
}
|
||||
}
|
||||
|
||||
void CModem::stop()
|
||||
{
|
||||
m_stopped = true;
|
||||
|
||||
Wait();
|
||||
}
|
||||
|
||||
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (C) 2013,2014 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef Modem_H
|
||||
#define Modem_H
|
||||
|
||||
#include "HeaderData.h"
|
||||
#include "RingBuffer.h"
|
||||
|
||||
#include <wx/wx.h>
|
||||
|
||||
enum DSMT_TYPE {
|
||||
DSMTT_NONE,
|
||||
DSMTT_START,
|
||||
DSMTT_HEADER,
|
||||
DSMTT_DATA,
|
||||
DSMTT_EOT,
|
||||
DSMTT_LOST
|
||||
};
|
||||
|
||||
class CModem : public wxThread {
|
||||
public:
|
||||
CModem();
|
||||
virtual ~CModem();
|
||||
|
||||
virtual bool start() = 0;
|
||||
|
||||
virtual bool writeHeader(const CHeaderData& header) = 0;
|
||||
virtual bool writeData(const unsigned char* data, unsigned int length, bool end) = 0;
|
||||
|
||||
virtual unsigned int getSpace() = 0;
|
||||
virtual bool isTXReady() = 0;
|
||||
virtual bool isTX();
|
||||
|
||||
virtual DSMT_TYPE read();
|
||||
virtual CHeaderData* readHeader();
|
||||
virtual unsigned int readData(unsigned char* data, unsigned int length);
|
||||
|
||||
virtual void stop();
|
||||
|
||||
protected:
|
||||
CRingBuffer<unsigned char> m_rxData;
|
||||
wxMutex m_mutex;
|
||||
bool m_tx;
|
||||
bool m_stopped;
|
||||
|
||||
private:
|
||||
DSMT_TYPE m_readType;
|
||||
unsigned int m_readLength;
|
||||
unsigned char* m_readBuffer;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Copyright (C) 2011,2013,2014 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "OutputQueue.h"
|
||||
|
||||
COutputQueue::COutputQueue(unsigned int space, unsigned int threshold) :
|
||||
m_data(space),
|
||||
m_threshold(threshold),
|
||||
m_header(NULL),
|
||||
m_count(0U)
|
||||
{
|
||||
wxASSERT(space > 0U);
|
||||
wxASSERT(threshold > 0U);
|
||||
}
|
||||
|
||||
COutputQueue::~COutputQueue()
|
||||
{
|
||||
delete m_header;
|
||||
}
|
||||
|
||||
void COutputQueue::setHeader(CHeaderData* header)
|
||||
{
|
||||
wxASSERT(header != NULL);
|
||||
|
||||
delete m_header;
|
||||
|
||||
m_header = header;
|
||||
}
|
||||
|
||||
CHeaderData* COutputQueue::getHeader()
|
||||
{
|
||||
CHeaderData* header = m_header;
|
||||
|
||||
m_header = NULL;
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
unsigned int COutputQueue::getData(unsigned char *data, unsigned int length, bool& end)
|
||||
{
|
||||
wxASSERT(data != NULL);
|
||||
|
||||
if (m_data.isEmpty()) {
|
||||
end = false;
|
||||
return 0U;
|
||||
}
|
||||
|
||||
unsigned char hdr[2U];
|
||||
m_data.getData(hdr, 2U);
|
||||
|
||||
end = hdr[0U] == 1U;
|
||||
|
||||
if (length < hdr[1U]) {
|
||||
wxLogWarning(wxT("Output buffer is too short, %u < %u"), length, hdr[1U]);
|
||||
|
||||
unsigned int len = m_data.getData(data, length);
|
||||
|
||||
// Purge the excess data
|
||||
unsigned int n = hdr[1U] - length;
|
||||
unsigned char c;
|
||||
for (unsigned int i = 0U; i < n; i++)
|
||||
m_data.getData(&c, 1U);
|
||||
|
||||
return len;
|
||||
} else {
|
||||
return m_data.getData(data, hdr[1U]);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int COutputQueue::addData(const unsigned char *data, unsigned int length, bool end)
|
||||
{
|
||||
wxASSERT(data != NULL);
|
||||
|
||||
bool ret = m_data.hasSpace(length + 2U);
|
||||
if (!ret) {
|
||||
// XXX wxLogWarning(wxT("Not enough space in the output queue"));
|
||||
return 0U;
|
||||
}
|
||||
|
||||
unsigned char hdr[2U];
|
||||
hdr[0U] = end ? 1U : 0U;
|
||||
hdr[1U] = length;
|
||||
m_data.addData(hdr, 2U);
|
||||
|
||||
if (end)
|
||||
m_count = m_threshold;
|
||||
else
|
||||
m_count++;
|
||||
|
||||
return m_data.addData(data, length);
|
||||
}
|
||||
|
||||
bool COutputQueue::headerReady() const
|
||||
{
|
||||
return m_header != NULL && m_count >= m_threshold;
|
||||
}
|
||||
|
||||
bool COutputQueue::dataReady() const
|
||||
{
|
||||
return m_header == NULL && m_count >= m_threshold;
|
||||
}
|
||||
|
||||
bool COutputQueue::isEmpty()
|
||||
{
|
||||
return m_header == NULL && m_data.isEmpty();
|
||||
}
|
||||
|
||||
void COutputQueue::reset()
|
||||
{
|
||||
m_data.clear();
|
||||
|
||||
delete m_header;
|
||||
m_header = NULL;
|
||||
|
||||
m_count = 0U;
|
||||
}
|
||||
|
||||
void COutputQueue::setThreshold(unsigned int threshold)
|
||||
{
|
||||
wxASSERT(threshold > 0U);
|
||||
|
||||
m_threshold = threshold;
|
||||
}
|
||||
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) 2011,2014 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef OutputQueue_H
|
||||
#define OutputQueue_H
|
||||
|
||||
#include "RingBuffer.h"
|
||||
#include "HeaderData.h"
|
||||
|
||||
#include <wx/wx.h>
|
||||
|
||||
class COutputQueue {
|
||||
public:
|
||||
COutputQueue(unsigned int space, unsigned int threshold);
|
||||
~COutputQueue();
|
||||
|
||||
void setHeader(CHeaderData* header);
|
||||
CHeaderData* getHeader();
|
||||
|
||||
unsigned int getData(unsigned char* data, unsigned int length, bool& end);
|
||||
unsigned int addData(const unsigned char* data, unsigned int length, bool end);
|
||||
|
||||
bool headerReady() const;
|
||||
bool dataReady() const;
|
||||
|
||||
bool isEmpty();
|
||||
|
||||
void reset();
|
||||
|
||||
void setThreshold(unsigned int threshold);
|
||||
|
||||
private:
|
||||
CRingBuffer<unsigned char> m_data;
|
||||
unsigned int m_threshold;
|
||||
CHeaderData* m_header;
|
||||
unsigned int m_count;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,539 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2014 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "RepeaterProtocolHandler.h"
|
||||
#include "CCITTChecksumReverse.h"
|
||||
#include "DStarDefines.h"
|
||||
#include "Utils.h"
|
||||
|
||||
// #define DUMP_TX
|
||||
|
||||
const unsigned int BUFFER_LENGTH = 255U;
|
||||
|
||||
CRepeaterProtocolHandler::CRepeaterProtocolHandler(const wxString& gatewayAddress, unsigned int gatewayPort, const wxString& localAddress, unsigned int localPort, const wxString& name) :
|
||||
m_socket(localAddress, localPort),
|
||||
m_address(),
|
||||
m_port(gatewayPort),
|
||||
m_name(name),
|
||||
m_outId(0U),
|
||||
m_outSeq(0U),
|
||||
m_type(NETWORK_NONE),
|
||||
m_inId(0U),
|
||||
m_buffer(NULL),
|
||||
m_length(0U)
|
||||
{
|
||||
m_address = CUDPReaderWriter::lookup(gatewayAddress);
|
||||
|
||||
m_buffer = new unsigned char[BUFFER_LENGTH];
|
||||
|
||||
wxDateTime now = wxDateTime::UNow();
|
||||
::srand(now.GetMillisecond());
|
||||
}
|
||||
|
||||
CRepeaterProtocolHandler::~CRepeaterProtocolHandler()
|
||||
{
|
||||
delete[] m_buffer;
|
||||
}
|
||||
|
||||
bool CRepeaterProtocolHandler::open()
|
||||
{
|
||||
if (m_address.s_addr == INADDR_NONE)
|
||||
return false;
|
||||
|
||||
return m_socket.open();
|
||||
}
|
||||
|
||||
bool CRepeaterProtocolHandler::writeHeader(const CHeaderData& header)
|
||||
{
|
||||
unsigned char buffer[50U];
|
||||
|
||||
buffer[0] = 'D';
|
||||
buffer[1] = 'S';
|
||||
buffer[2] = 'R';
|
||||
buffer[3] = 'P';
|
||||
|
||||
buffer[4] = 0x20U;
|
||||
|
||||
// Create a random id for this transmission
|
||||
m_outId = (::rand() % 65535U) + 1U;
|
||||
|
||||
buffer[5] = m_outId / 256U; // Unique session id
|
||||
buffer[6] = m_outId % 256U;
|
||||
|
||||
buffer[7] = 0U;
|
||||
|
||||
buffer[8] = header.getFlag1();
|
||||
buffer[9] = header.getFlag2();
|
||||
buffer[10] = header.getFlag3();
|
||||
|
||||
for (unsigned int i = 0U; i < LONG_CALLSIGN_LENGTH; i++)
|
||||
buffer[11 + i] = header.getRptCall1().GetChar(i);
|
||||
|
||||
for (unsigned int i = 0U; i < LONG_CALLSIGN_LENGTH; i++)
|
||||
buffer[19 + i] = header.getRptCall2().GetChar(i);
|
||||
|
||||
for (unsigned int i = 0U; i < LONG_CALLSIGN_LENGTH; i++)
|
||||
buffer[27 + i] = header.getYourCall().GetChar(i);
|
||||
|
||||
for (unsigned int i = 0U; i < LONG_CALLSIGN_LENGTH; i++)
|
||||
buffer[35 + i] = header.getMyCall1().GetChar(i);
|
||||
|
||||
for (unsigned int i = 0U; i < SHORT_CALLSIGN_LENGTH; i++)
|
||||
buffer[43 + i] = header.getMyCall2().GetChar(i);
|
||||
|
||||
// Get the checksum for the header
|
||||
CCCITTChecksumReverse csum;
|
||||
csum.update(buffer + 8U, 4U * LONG_CALLSIGN_LENGTH + SHORT_CALLSIGN_LENGTH + 3U);
|
||||
csum.result(buffer + 47U);
|
||||
|
||||
m_outSeq = 0U;
|
||||
|
||||
#if defined(DUMP_TX)
|
||||
CUtils::dump(wxT("Sending Header"), buffer, 49U);
|
||||
#endif
|
||||
|
||||
for (unsigned int i = 0U; i < 2U; i++) {
|
||||
bool ret = m_socket.write(buffer, 49U, m_address, m_port);
|
||||
if (!ret)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CRepeaterProtocolHandler::writeData(const unsigned char* data, unsigned int length, unsigned int errors, bool end)
|
||||
{
|
||||
wxASSERT(data != NULL);
|
||||
wxASSERT(length == DV_FRAME_LENGTH_BYTES || length == DV_FRAME_MAX_LENGTH_BYTES);
|
||||
|
||||
unsigned char buffer[30U];
|
||||
|
||||
buffer[0] = 'D';
|
||||
buffer[1] = 'S';
|
||||
buffer[2] = 'R';
|
||||
buffer[3] = 'P';
|
||||
|
||||
buffer[4] = 0x21U;
|
||||
|
||||
buffer[5] = m_outId / 256U; // Unique session id
|
||||
buffer[6] = m_outId % 256U;
|
||||
|
||||
// If this is a data sync, reset the sequence to zero
|
||||
if (data[9] == 0x55 && data[10] == 0x2D && data[11] == 0x16)
|
||||
m_outSeq = 0U;
|
||||
|
||||
buffer[7] = m_outSeq;
|
||||
if (end)
|
||||
buffer[7] |= 0x40U; // End of data marker
|
||||
|
||||
buffer[8] = errors;
|
||||
|
||||
m_outSeq++;
|
||||
if (m_outSeq > 0x14U)
|
||||
m_outSeq = 0U;
|
||||
|
||||
::memcpy(buffer + 9U, data, length);
|
||||
|
||||
#if defined(DUMP_TX)
|
||||
CUtils::dump(wxT("Sending Data"), buffer, length + 9U);
|
||||
#endif
|
||||
|
||||
return m_socket.write(buffer, length + 9U, m_address, m_port);
|
||||
}
|
||||
|
||||
bool CRepeaterProtocolHandler::writeBusyHeader(const CHeaderData& header)
|
||||
{
|
||||
unsigned char buffer[50U];
|
||||
|
||||
buffer[0] = 'D';
|
||||
buffer[1] = 'S';
|
||||
buffer[2] = 'R';
|
||||
buffer[3] = 'P';
|
||||
|
||||
buffer[4] = 0x22U;
|
||||
|
||||
// Create a random id for this header
|
||||
m_outId = (::rand() % 65535U) + 1U;
|
||||
|
||||
buffer[5] = m_outId / 256U; // Unique session id
|
||||
buffer[6] = m_outId % 256U;
|
||||
|
||||
buffer[7] = 0U;
|
||||
|
||||
buffer[8] = header.getFlag1();
|
||||
buffer[9] = header.getFlag2();
|
||||
buffer[10] = header.getFlag3();
|
||||
|
||||
for (unsigned int i = 0U; i < LONG_CALLSIGN_LENGTH; i++)
|
||||
buffer[11 + i] = header.getRptCall1().GetChar(i);
|
||||
|
||||
for (unsigned int i = 0U; i < LONG_CALLSIGN_LENGTH; i++)
|
||||
buffer[19 + i] = header.getRptCall2().GetChar(i);
|
||||
|
||||
for (unsigned int i = 0U; i < LONG_CALLSIGN_LENGTH; i++)
|
||||
buffer[27 + i] = header.getYourCall().GetChar(i);
|
||||
|
||||
for (unsigned int i = 0U; i < LONG_CALLSIGN_LENGTH; i++)
|
||||
buffer[35 + i] = header.getMyCall1().GetChar(i);
|
||||
|
||||
for (unsigned int i = 0U; i < SHORT_CALLSIGN_LENGTH; i++)
|
||||
buffer[43 + i] = header.getMyCall2().GetChar(i);
|
||||
|
||||
// Get the checksum for the header
|
||||
CCCITTChecksumReverse csum;
|
||||
csum.update(buffer + 8U, 4U * LONG_CALLSIGN_LENGTH + SHORT_CALLSIGN_LENGTH + 3U);
|
||||
csum.result(buffer + 47U);
|
||||
|
||||
m_outSeq = 0U;
|
||||
|
||||
#if defined(DUMP_TX)
|
||||
CUtils::dump(wxT("Sending Busy Header"), buffer, 49U);
|
||||
#endif
|
||||
|
||||
return m_socket.write(buffer, 49U, m_address, m_port);
|
||||
}
|
||||
|
||||
bool CRepeaterProtocolHandler::writeBusyData(const unsigned char* data, unsigned int length, unsigned int errors, bool end)
|
||||
{
|
||||
wxASSERT(data != NULL);
|
||||
wxASSERT(length == DV_FRAME_LENGTH_BYTES || length == DV_FRAME_MAX_LENGTH_BYTES);
|
||||
|
||||
unsigned char buffer[30U];
|
||||
|
||||
buffer[0] = 'D';
|
||||
buffer[1] = 'S';
|
||||
buffer[2] = 'R';
|
||||
buffer[3] = 'P';
|
||||
|
||||
buffer[4] = 0x23U;
|
||||
|
||||
buffer[5] = m_outId / 256U; // Unique session id
|
||||
buffer[6] = m_outId % 256U;
|
||||
|
||||
// If this is a data sync, reset the sequence to zero
|
||||
if (data[9] == 0x55 && data[10] == 0x2D && data[11] == 0x16)
|
||||
m_outSeq = 0U;
|
||||
|
||||
buffer[7] = m_outSeq;
|
||||
if (end)
|
||||
buffer[7] |= 0x40U; // End of data marker
|
||||
|
||||
buffer[8] = errors;
|
||||
|
||||
m_outSeq++;
|
||||
if (m_outSeq > 0x14U)
|
||||
m_outSeq = 0U;
|
||||
|
||||
::memcpy(buffer + 9U, data, length);
|
||||
|
||||
#if defined(DUMP_TX)
|
||||
CUtils::dump(wxT("Sending Busy Data"), buffer, length + 9U);
|
||||
#endif
|
||||
|
||||
return m_socket.write(buffer, length + 9U, m_address, m_port);
|
||||
}
|
||||
|
||||
bool CRepeaterProtocolHandler::writePoll(const wxString& text)
|
||||
{
|
||||
unsigned char buffer[40U];
|
||||
|
||||
buffer[0] = 'D';
|
||||
buffer[1] = 'S';
|
||||
buffer[2] = 'R';
|
||||
buffer[3] = 'P';
|
||||
|
||||
buffer[4] = 0x0A; // Poll with text
|
||||
|
||||
unsigned int length = text.Length();
|
||||
|
||||
for (unsigned int i = 0U; i < length; i++)
|
||||
buffer[5U + i] = text.GetChar(i);
|
||||
|
||||
buffer[5U + length] = 0x00;
|
||||
|
||||
#if defined(DUMP_TX)
|
||||
CUtils::dump(wxT("Sending Poll"), buffer, 6U + length);
|
||||
#endif
|
||||
|
||||
return m_socket.write(buffer, 6U + length, m_address, m_port);
|
||||
}
|
||||
|
||||
bool CRepeaterProtocolHandler::writeRegister()
|
||||
{
|
||||
unsigned char buffer[40U];
|
||||
|
||||
buffer[0] = 'D';
|
||||
buffer[1] = 'S';
|
||||
buffer[2] = 'R';
|
||||
buffer[3] = 'P';
|
||||
|
||||
buffer[4] = 0x0B; // Register with name
|
||||
|
||||
unsigned int length = m_name.Length();
|
||||
|
||||
for (unsigned int i = 0U; i < length; i++)
|
||||
buffer[5U + i] = m_name.GetChar(i);
|
||||
|
||||
buffer[5U + length] = 0x00;
|
||||
|
||||
#if defined(DUMP_TX)
|
||||
CUtils::dump(wxT("Sending Register"), buffer, 6U + length);
|
||||
#endif
|
||||
|
||||
return m_socket.write(buffer, 6U + length, m_address, m_port);
|
||||
}
|
||||
|
||||
NETWORK_TYPE CRepeaterProtocolHandler::read()
|
||||
{
|
||||
bool res = true;
|
||||
|
||||
// Loop until we have no more data from the socket or we have data for the higher layers
|
||||
while (res)
|
||||
res = readPackets();
|
||||
|
||||
return m_type;
|
||||
}
|
||||
|
||||
bool CRepeaterProtocolHandler::readPackets()
|
||||
{
|
||||
m_type = NETWORK_NONE;
|
||||
|
||||
// No more data?
|
||||
in_addr address;
|
||||
unsigned int port;
|
||||
int length = m_socket.read(m_buffer, BUFFER_LENGTH, address, port);
|
||||
if (length <= 0)
|
||||
return false;
|
||||
|
||||
// Check if the data is for us
|
||||
if (m_address.s_addr != address.s_addr || m_port != port) {
|
||||
wxLogMessage(wxT("Packet received from an invalid source, %08X != %08X and/or %u != %u"), m_address.s_addr, address.s_addr, m_port, port);
|
||||
CUtils::dump(wxT("Data"), m_buffer, length);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_length = length;
|
||||
|
||||
// Invalid packet type?
|
||||
if (m_buffer[0] == 'D' && m_buffer[1] == 'S' && m_buffer[2] == 'R' && m_buffer[3] == 'P') {
|
||||
if (m_buffer[4] == 0x00U) {
|
||||
m_type = NETWORK_TEXT;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_buffer[4] == 0x01U) {
|
||||
m_type = NETWORK_TEMPTEXT;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Status data 1
|
||||
else if (m_buffer[4] == 0x04U && m_buffer[5] == 0x00U) {
|
||||
m_type = NETWORK_STATUS1;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Status data 2
|
||||
else if (m_buffer[4] == 0x04U && m_buffer[5] == 0x01U) {
|
||||
m_type = NETWORK_STATUS2;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Status data 3
|
||||
else if (m_buffer[4] == 0x04U && m_buffer[5] == 0x02U) {
|
||||
m_type = NETWORK_STATUS3;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Status data 4
|
||||
else if (m_buffer[4] == 0x04U && m_buffer[5] == 0x03U) {
|
||||
m_type = NETWORK_STATUS4;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Status data 5
|
||||
else if (m_buffer[4] == 0x04U && m_buffer[5] == 0x04U) {
|
||||
m_type = NETWORK_STATUS5;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Header data
|
||||
else if (m_buffer[4] == 0x20U) {
|
||||
wxUint16 id = m_buffer[5] * 256U + m_buffer[6];
|
||||
|
||||
// Are we listening for headers?
|
||||
if (m_inId != 0U)
|
||||
return true;
|
||||
|
||||
m_inId = id; // Take the stream id
|
||||
m_type = NETWORK_HEADER;
|
||||
return false;
|
||||
}
|
||||
|
||||
// User data
|
||||
else if (m_buffer[4] == 0x21U) {
|
||||
wxUint16 id = m_buffer[5] * 256U + m_buffer[6];
|
||||
|
||||
// Check that the stream id matches the valid header, reject otherwise
|
||||
if (id != m_inId)
|
||||
return true;
|
||||
|
||||
// Is this the last packet in the stream?
|
||||
if ((m_buffer[7] & 0x40) == 0x40)
|
||||
m_inId = 0U;
|
||||
|
||||
m_type = NETWORK_DATA;
|
||||
return false;
|
||||
}
|
||||
|
||||
else if (m_buffer[4] == 0x24U) {
|
||||
// Silently ignore DD data
|
||||
}
|
||||
}
|
||||
|
||||
CUtils::dump(wxT("Unknown packet from the Gateway"), m_buffer, m_length);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
CHeaderData* CRepeaterProtocolHandler::readHeader()
|
||||
{
|
||||
if (m_type != NETWORK_HEADER)
|
||||
return NULL;
|
||||
|
||||
// If the checksum is 0xFFFF then we accept the header without testing the checksum
|
||||
if (m_buffer[47U] == 0xFFU && m_buffer[48U] == 0xFFU)
|
||||
return new CHeaderData(m_buffer + 8U, RADIO_HEADER_LENGTH_BYTES, false);
|
||||
|
||||
// Header checksum testing is enabled
|
||||
CHeaderData* header = new CHeaderData(m_buffer + 8U, RADIO_HEADER_LENGTH_BYTES, true);
|
||||
|
||||
if (!header->isValid()) {
|
||||
CUtils::dump(wxT("Header checksum failure from the Gateway"), m_buffer + 8U, RADIO_HEADER_LENGTH_BYTES);
|
||||
delete header;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
unsigned int CRepeaterProtocolHandler::readData(unsigned char* buffer, unsigned int length, unsigned char& seqNo)
|
||||
{
|
||||
if (m_type != NETWORK_DATA)
|
||||
return 0U;
|
||||
|
||||
unsigned int dataLen = m_length - 9U;
|
||||
|
||||
// Is our buffer too small?
|
||||
if (dataLen > length)
|
||||
dataLen = length;
|
||||
|
||||
seqNo = m_buffer[7U];
|
||||
|
||||
::memcpy(buffer, m_buffer + 9U, dataLen);
|
||||
|
||||
// Simple sanity checks of the incoming sync bits
|
||||
if (seqNo == 0U) {
|
||||
// Regenerate sync bytes
|
||||
buffer[9U] = DATA_SYNC_BYTES[0U];
|
||||
buffer[10U] = DATA_SYNC_BYTES[1U];
|
||||
buffer[11U] = DATA_SYNC_BYTES[2U];
|
||||
} else if (::memcmp(buffer + 9U, DATA_SYNC_BYTES, DATA_FRAME_LENGTH_BYTES) == 0) {
|
||||
// Sync bytes appearing where they shouldn't!
|
||||
buffer[9U] = 0x70U;
|
||||
buffer[10U] = 0x4FU;
|
||||
buffer[11U] = 0x93U;
|
||||
}
|
||||
|
||||
return dataLen;
|
||||
}
|
||||
|
||||
void CRepeaterProtocolHandler::readText(wxString& text, LINK_STATUS& status, wxString& reflector)
|
||||
{
|
||||
if (m_type != NETWORK_TEXT) {
|
||||
text = wxT(" ");
|
||||
reflector = wxT(" ");
|
||||
status = LS_NONE;
|
||||
return;
|
||||
}
|
||||
|
||||
text = wxString((char*)(m_buffer + 5U), wxConvLocal, 20U);
|
||||
|
||||
status = LINK_STATUS(m_buffer[25U]);
|
||||
|
||||
reflector = wxString((char*)(m_buffer + 26U), wxConvLocal, 8U);
|
||||
}
|
||||
|
||||
void CRepeaterProtocolHandler::readTempText(wxString& text)
|
||||
{
|
||||
if (m_type != NETWORK_TEMPTEXT) {
|
||||
text = wxT(" ");
|
||||
return;
|
||||
}
|
||||
|
||||
text = wxString((char*)(m_buffer + 5U), wxConvLocal, 20U);
|
||||
}
|
||||
|
||||
wxString CRepeaterProtocolHandler::readStatus1()
|
||||
{
|
||||
if (m_type != NETWORK_STATUS1)
|
||||
return wxEmptyString;
|
||||
|
||||
return wxString((char*)(m_buffer + 6U), wxConvLocal, 20U);
|
||||
}
|
||||
|
||||
wxString CRepeaterProtocolHandler::readStatus2()
|
||||
{
|
||||
if (m_type != NETWORK_STATUS2)
|
||||
return wxEmptyString;
|
||||
|
||||
return wxString((char*)(m_buffer + 6U), wxConvLocal, 20U);
|
||||
}
|
||||
|
||||
wxString CRepeaterProtocolHandler::readStatus3()
|
||||
{
|
||||
if (m_type != NETWORK_STATUS3)
|
||||
return wxEmptyString;
|
||||
|
||||
return wxString((char*)(m_buffer + 6U), wxConvLocal, 20U);
|
||||
}
|
||||
|
||||
wxString CRepeaterProtocolHandler::readStatus4()
|
||||
{
|
||||
if (m_type != NETWORK_STATUS4)
|
||||
return wxEmptyString;
|
||||
|
||||
return wxString((char*)(m_buffer + 6U), wxConvLocal, 20U);
|
||||
}
|
||||
|
||||
wxString CRepeaterProtocolHandler::readStatus5()
|
||||
{
|
||||
if (m_type != NETWORK_STATUS5)
|
||||
return wxEmptyString;
|
||||
|
||||
return wxString((char*)(m_buffer + 6U), wxConvLocal, 20U);
|
||||
}
|
||||
|
||||
void CRepeaterProtocolHandler::reset()
|
||||
{
|
||||
m_inId = 0U;
|
||||
}
|
||||
|
||||
void CRepeaterProtocolHandler::close()
|
||||
{
|
||||
m_socket.close();
|
||||
}
|
||||
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2014 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef RepeaterProtocolHander_H
|
||||
#define RepeaterProtocolHander_H
|
||||
|
||||
#include "UDPReaderWriter.h"
|
||||
#include "DStarDefines.h"
|
||||
#include "HeaderData.h"
|
||||
|
||||
#include <wx/wx.h>
|
||||
|
||||
class CRepeaterProtocolHandler {
|
||||
public:
|
||||
CRepeaterProtocolHandler(const wxString& gatewayAddress, unsigned int gatewayPort, const wxString& localAddress, unsigned int localPort, const wxString& name);
|
||||
~CRepeaterProtocolHandler();
|
||||
|
||||
bool open();
|
||||
|
||||
bool writeHeader(const CHeaderData& header);
|
||||
bool writeBusyHeader(const CHeaderData& header);
|
||||
bool writeData(const unsigned char* data, unsigned int length, unsigned int errors, bool end);
|
||||
bool writeBusyData(const unsigned char* data, unsigned int length, unsigned int errors, bool end);
|
||||
bool writePoll(const wxString& text);
|
||||
bool writeRegister();
|
||||
|
||||
NETWORK_TYPE read();
|
||||
void readText(wxString& text, LINK_STATUS& status, wxString& reflector);
|
||||
void readTempText(wxString& text);
|
||||
wxString readStatus1();
|
||||
wxString readStatus2();
|
||||
wxString readStatus3();
|
||||
wxString readStatus4();
|
||||
wxString readStatus5();
|
||||
CHeaderData* readHeader();
|
||||
unsigned int readData(unsigned char* data, unsigned int length, unsigned char& seqNo);
|
||||
|
||||
void reset();
|
||||
|
||||
void close();
|
||||
|
||||
private:
|
||||
CUDPReaderWriter m_socket;
|
||||
in_addr m_address;
|
||||
unsigned int m_port;
|
||||
wxString m_name;
|
||||
wxUint16 m_outId;
|
||||
wxUint8 m_outSeq;
|
||||
NETWORK_TYPE m_type;
|
||||
wxUint16 m_inId;
|
||||
unsigned char* m_buffer;
|
||||
unsigned int m_length;
|
||||
|
||||
bool readPackets();
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright (C) 2006-2009,2012,2013,2015 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef RingBuffer_H
|
||||
#define RingBuffer_H
|
||||
|
||||
#include <wx/wx.h>
|
||||
|
||||
template<class T> class CRingBuffer {
|
||||
public:
|
||||
CRingBuffer(unsigned int length) :
|
||||
m_length(length),
|
||||
m_buffer(NULL),
|
||||
m_iPtr(0U),
|
||||
m_oPtr(0U)
|
||||
{
|
||||
wxASSERT(length > 0U);
|
||||
|
||||
m_buffer = new T[length];
|
||||
|
||||
::memset(m_buffer, 0x00, m_length * sizeof(T));
|
||||
}
|
||||
|
||||
~CRingBuffer()
|
||||
{
|
||||
delete[] m_buffer;
|
||||
}
|
||||
|
||||
unsigned int addData(const T* buffer, unsigned int nSamples)
|
||||
{
|
||||
if (nSamples > freeSpace())
|
||||
return 0U;
|
||||
|
||||
for (unsigned int i = 0U; i < nSamples; i++) {
|
||||
m_buffer[m_iPtr++] = buffer[i];
|
||||
|
||||
if (m_iPtr == m_length)
|
||||
m_iPtr = 0U;
|
||||
}
|
||||
|
||||
return nSamples;
|
||||
}
|
||||
|
||||
unsigned int getData(T* buffer, unsigned int nSamples)
|
||||
{
|
||||
unsigned int data = dataSize();
|
||||
|
||||
if (data < nSamples)
|
||||
nSamples = data;
|
||||
|
||||
for (unsigned int i = 0U; i < nSamples; i++) {
|
||||
buffer[i] = m_buffer[m_oPtr++];
|
||||
|
||||
if (m_oPtr == m_length)
|
||||
m_oPtr = 0U;
|
||||
}
|
||||
|
||||
return nSamples;
|
||||
}
|
||||
|
||||
unsigned int peek(T* buffer, unsigned int nSamples)
|
||||
{
|
||||
unsigned int data = dataSize();
|
||||
|
||||
if (data < nSamples)
|
||||
nSamples = data;
|
||||
|
||||
unsigned int ptr = m_oPtr;
|
||||
for (unsigned int i = 0U; i < nSamples; i++) {
|
||||
buffer[i] = m_buffer[ptr++];
|
||||
|
||||
if (ptr == m_length)
|
||||
ptr = 0U;
|
||||
}
|
||||
|
||||
return nSamples;
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
m_iPtr = 0U;
|
||||
m_oPtr = 0U;
|
||||
|
||||
::memset(m_buffer, 0x00, m_length * sizeof(T));
|
||||
}
|
||||
|
||||
unsigned int freeSpace() const
|
||||
{
|
||||
if (m_oPtr == m_iPtr)
|
||||
return m_length - 1U;
|
||||
|
||||
if (m_oPtr > m_iPtr)
|
||||
return m_oPtr - m_iPtr - 1U;
|
||||
|
||||
return m_length - (m_iPtr - m_oPtr) - 1U;
|
||||
}
|
||||
|
||||
bool hasSpace(unsigned int length) const
|
||||
{
|
||||
return freeSpace() > length;
|
||||
}
|
||||
|
||||
bool hasData() const
|
||||
{
|
||||
return m_oPtr != m_iPtr;
|
||||
}
|
||||
|
||||
bool isEmpty() const
|
||||
{
|
||||
return m_oPtr == m_iPtr;
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned int m_length;
|
||||
T* m_buffer;
|
||||
volatile unsigned int m_iPtr;
|
||||
volatile unsigned int m_oPtr;
|
||||
|
||||
unsigned int dataSize() const
|
||||
{
|
||||
if (m_iPtr >= m_oPtr)
|
||||
return m_iPtr - m_oPtr;
|
||||
|
||||
return m_length - (m_oPtr - m_iPtr);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,464 @@
|
||||
/*
|
||||
* Copyright (C) 2002-2004,2007-2011,2013,2014,2015 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 1999-2001 by Thomas Sailor HB9JNX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "SerialDataController.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#if defined(__WINDOWS__)
|
||||
#include <setupapi.h>
|
||||
#include <winioctl.h>
|
||||
#else
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <cerrno>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <termios.h>
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(__WINDOWS__)
|
||||
|
||||
const unsigned int BUFFER_LENGTH = 1000U;
|
||||
|
||||
CSerialDataController::CSerialDataController(const wxString& device, SERIAL_SPEED speed, bool assertRTS) :
|
||||
m_device(device),
|
||||
m_speed(speed),
|
||||
m_assertRTS(assertRTS),
|
||||
m_handle(INVALID_HANDLE_VALUE),
|
||||
m_readOverlapped(),
|
||||
m_writeOverlapped(),
|
||||
m_readBuffer(NULL),
|
||||
m_readLength(0U),
|
||||
m_readPending(false)
|
||||
{
|
||||
wxASSERT(!device.IsEmpty());
|
||||
|
||||
m_readBuffer = new unsigned char[BUFFER_LENGTH];
|
||||
}
|
||||
|
||||
CSerialDataController::~CSerialDataController()
|
||||
{
|
||||
delete[] m_readBuffer;
|
||||
}
|
||||
|
||||
bool CSerialDataController::open()
|
||||
{
|
||||
wxASSERT(m_handle == INVALID_HANDLE_VALUE);
|
||||
|
||||
DWORD errCode;
|
||||
|
||||
wxString baseName = m_device.Mid(4U); // Convert "\\.\COM10" to "COM10"
|
||||
|
||||
m_handle = ::CreateFile(m_device.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
|
||||
if (m_handle == INVALID_HANDLE_VALUE) {
|
||||
wxLogError(wxT("Cannot open device - %s, err=%04lx"), m_device.c_str(), ::GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
DCB dcb;
|
||||
if (::GetCommState(m_handle, &dcb) == 0) {
|
||||
wxLogError(wxT("Cannot get the attributes for %s, err=%04lx"), m_device.c_str(), ::GetLastError());
|
||||
::ClearCommError(m_handle, &errCode, NULL);
|
||||
::CloseHandle(m_handle);
|
||||
return false;
|
||||
}
|
||||
|
||||
dcb.BaudRate = DWORD(m_speed);
|
||||
dcb.ByteSize = 8;
|
||||
dcb.Parity = NOPARITY;
|
||||
dcb.fParity = FALSE;
|
||||
dcb.StopBits = ONESTOPBIT;
|
||||
dcb.fInX = FALSE;
|
||||
dcb.fOutX = FALSE;
|
||||
dcb.fOutxCtsFlow = FALSE;
|
||||
dcb.fOutxDsrFlow = FALSE;
|
||||
dcb.fDtrControl = DTR_CONTROL_DISABLE;
|
||||
dcb.fRtsControl = RTS_CONTROL_DISABLE;
|
||||
|
||||
if (::SetCommState(m_handle, &dcb) == 0) {
|
||||
wxLogError(wxT("Cannot set the attributes for %s, err=%04lx"), m_device.c_str(), ::GetLastError());
|
||||
::ClearCommError(m_handle, &errCode, NULL);
|
||||
::CloseHandle(m_handle);
|
||||
return false;
|
||||
}
|
||||
|
||||
COMMTIMEOUTS timeouts;
|
||||
if (!::GetCommTimeouts(m_handle, &timeouts)) {
|
||||
wxLogError(wxT("Cannot get the timeouts for %s, err=%04lx"), m_device.c_str(), ::GetLastError());
|
||||
::ClearCommError(m_handle, &errCode, NULL);
|
||||
::CloseHandle(m_handle);
|
||||
return false;
|
||||
}
|
||||
|
||||
timeouts.ReadIntervalTimeout = MAXDWORD;
|
||||
timeouts.ReadTotalTimeoutMultiplier = 0UL;
|
||||
timeouts.ReadTotalTimeoutConstant = 0UL;
|
||||
|
||||
if (!::SetCommTimeouts(m_handle, &timeouts)) {
|
||||
wxLogError(wxT("Cannot set the timeouts for %s, err=%04lx"), m_device.c_str(), ::GetLastError());
|
||||
::ClearCommError(m_handle, &errCode, NULL);
|
||||
::CloseHandle(m_handle);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (::EscapeCommFunction(m_handle, CLRDTR) == 0) {
|
||||
wxLogError(wxT("Cannot clear DTR for %s, err=%04lx"), m_device.c_str(), ::GetLastError());
|
||||
::ClearCommError(m_handle, &errCode, NULL);
|
||||
::CloseHandle(m_handle);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (::EscapeCommFunction(m_handle, m_assertRTS ? SETRTS : CLRRTS) == 0) {
|
||||
wxLogError(wxT("Cannot set/clear RTS for %s, err=%04lx"), m_device.c_str(), ::GetLastError());
|
||||
::ClearCommError(m_handle, &errCode, NULL);
|
||||
::CloseHandle(m_handle);
|
||||
return false;
|
||||
}
|
||||
|
||||
::ClearCommError(m_handle, &errCode, NULL);
|
||||
|
||||
::memset(&m_readOverlapped, 0x00U, sizeof(OVERLAPPED));
|
||||
::memset(&m_writeOverlapped, 0x00U, sizeof(OVERLAPPED));
|
||||
|
||||
m_readOverlapped.hEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
m_writeOverlapped.hEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
|
||||
m_readLength = 0U;
|
||||
m_readPending = false;
|
||||
::memset(m_readBuffer, 0x00U, BUFFER_LENGTH);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int CSerialDataController::read(unsigned char* buffer, unsigned int length)
|
||||
{
|
||||
wxASSERT(m_handle != INVALID_HANDLE_VALUE);
|
||||
wxASSERT(buffer != NULL);
|
||||
|
||||
unsigned int ptr = 0U;
|
||||
|
||||
while (ptr < length) {
|
||||
int ret = readNonblock(buffer + ptr, length - ptr);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
} else if (ret == 0) {
|
||||
if (ptr == 0U)
|
||||
return 0;
|
||||
} else {
|
||||
ptr += ret;
|
||||
}
|
||||
}
|
||||
|
||||
return int(length);
|
||||
}
|
||||
|
||||
int CSerialDataController::readNonblock(unsigned char* buffer, unsigned int length)
|
||||
{
|
||||
wxASSERT(m_handle != INVALID_HANDLE_VALUE);
|
||||
wxASSERT(buffer != NULL);
|
||||
|
||||
if (length > BUFFER_LENGTH)
|
||||
length = BUFFER_LENGTH;
|
||||
|
||||
if (m_readPending && length != m_readLength) {
|
||||
::CancelIo(m_handle);
|
||||
m_readPending = false;
|
||||
}
|
||||
|
||||
m_readLength = length;
|
||||
|
||||
if (length == 0U)
|
||||
return 0;
|
||||
|
||||
if (!m_readPending) {
|
||||
DWORD bytes = 0UL;
|
||||
BOOL res = ::ReadFile(m_handle, m_readBuffer, m_readLength, &bytes, &m_readOverlapped);
|
||||
if (res) {
|
||||
::memcpy(buffer, m_readBuffer, bytes);
|
||||
return int(bytes);
|
||||
}
|
||||
|
||||
DWORD error = ::GetLastError();
|
||||
if (error != ERROR_IO_PENDING) {
|
||||
wxLogError(wxT("Error from ReadFile: %04lx"), error);
|
||||
return -1;
|
||||
}
|
||||
|
||||
m_readPending = true;
|
||||
}
|
||||
|
||||
BOOL res = HasOverlappedIoCompleted(&m_readOverlapped);
|
||||
if (!res)
|
||||
return 0;
|
||||
|
||||
DWORD bytes = 0UL;
|
||||
res = ::GetOverlappedResult(m_handle, &m_readOverlapped, &bytes, TRUE);
|
||||
if (!res) {
|
||||
wxLogError(wxT("Error from GetOverlappedResult (ReadFile): %04lx"), ::GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
::memcpy(buffer, m_readBuffer, bytes);
|
||||
m_readPending = false;
|
||||
|
||||
return int(bytes);
|
||||
}
|
||||
|
||||
int CSerialDataController::write(const unsigned char* buffer, unsigned int length)
|
||||
{
|
||||
wxASSERT(m_handle != INVALID_HANDLE_VALUE);
|
||||
wxASSERT(buffer != NULL);
|
||||
|
||||
if (length == 0U)
|
||||
return 0;
|
||||
|
||||
unsigned int ptr = 0U;
|
||||
|
||||
while (ptr < length) {
|
||||
DWORD bytes = 0UL;
|
||||
BOOL res = ::WriteFile(m_handle, buffer + ptr, length - ptr, &bytes, &m_writeOverlapped);
|
||||
if (!res) {
|
||||
DWORD error = ::GetLastError();
|
||||
if (error != ERROR_IO_PENDING) {
|
||||
wxLogError(wxT("Error from WriteFile: %04lx"), error);
|
||||
return -1;
|
||||
}
|
||||
|
||||
res = ::GetOverlappedResult(m_handle, &m_writeOverlapped, &bytes, TRUE);
|
||||
if (!res) {
|
||||
wxLogError(wxT("Error from GetOverlappedResult (WriteFile): %04lx"), ::GetLastError());
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
ptr += bytes;
|
||||
}
|
||||
|
||||
return int(length);
|
||||
}
|
||||
|
||||
void CSerialDataController::close()
|
||||
{
|
||||
wxASSERT(m_handle != INVALID_HANDLE_VALUE);
|
||||
|
||||
::CloseHandle(m_handle);
|
||||
m_handle = INVALID_HANDLE_VALUE;
|
||||
|
||||
::CloseHandle(m_readOverlapped.hEvent);
|
||||
::CloseHandle(m_writeOverlapped.hEvent);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
CSerialDataController::CSerialDataController(const wxString& device, SERIAL_SPEED speed, bool assertRTS) :
|
||||
m_device(device),
|
||||
m_speed(speed),
|
||||
m_assertRTS(assertRTS),
|
||||
m_fd(-1)
|
||||
{
|
||||
wxASSERT(!device.IsEmpty());
|
||||
}
|
||||
|
||||
CSerialDataController::~CSerialDataController()
|
||||
{
|
||||
}
|
||||
|
||||
bool CSerialDataController::open()
|
||||
{
|
||||
wxASSERT(m_fd == -1);
|
||||
|
||||
m_fd = ::open(m_device.mb_str(), O_RDWR | O_NOCTTY | O_NDELAY, 0);
|
||||
if (m_fd < 0) {
|
||||
wxLogError(wxT("Cannot open device - %s"), m_device.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (::isatty(m_fd) == 0) {
|
||||
wxLogError(wxT("%s is not a TTY device"), m_device.c_str());
|
||||
::close(m_fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
termios termios;
|
||||
if (::tcgetattr(m_fd, &termios) < 0) {
|
||||
wxLogError(wxT("Cannot get the attributes for %s"), m_device.c_str());
|
||||
::close(m_fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
termios.c_lflag &= ~(ECHO | ECHOE | ICANON | IEXTEN | ISIG);
|
||||
termios.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON | IXOFF | IXANY);
|
||||
termios.c_cflag &= ~(CSIZE | CSTOPB | PARENB | CRTSCTS);
|
||||
termios.c_cflag |= CS8;
|
||||
termios.c_oflag &= ~(OPOST);
|
||||
termios.c_cc[VMIN] = 0;
|
||||
termios.c_cc[VTIME] = 10;
|
||||
|
||||
switch (m_speed) {
|
||||
case SERIAL_1200:
|
||||
::cfsetospeed(&termios, B1200);
|
||||
::cfsetispeed(&termios, B1200);
|
||||
break;
|
||||
case SERIAL_2400:
|
||||
::cfsetospeed(&termios, B2400);
|
||||
::cfsetispeed(&termios, B2400);
|
||||
break;
|
||||
case SERIAL_4800:
|
||||
::cfsetospeed(&termios, B4800);
|
||||
::cfsetispeed(&termios, B4800);
|
||||
break;
|
||||
case SERIAL_9600:
|
||||
::cfsetospeed(&termios, B9600);
|
||||
::cfsetispeed(&termios, B9600);
|
||||
break;
|
||||
case SERIAL_19200:
|
||||
::cfsetospeed(&termios, B19200);
|
||||
::cfsetispeed(&termios, B19200);
|
||||
break;
|
||||
case SERIAL_38400:
|
||||
::cfsetospeed(&termios, B38400);
|
||||
::cfsetispeed(&termios, B38400);
|
||||
break;
|
||||
case SERIAL_115200:
|
||||
::cfsetospeed(&termios, B115200);
|
||||
::cfsetispeed(&termios, B115200);
|
||||
break;
|
||||
case SERIAL_230400:
|
||||
::cfsetospeed(&termios, B230400);
|
||||
::cfsetispeed(&termios, B230400);
|
||||
break;
|
||||
default:
|
||||
wxLogError(wxT("Unsupported serial port speed - %d"), int(m_speed));
|
||||
::close(m_fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (::tcsetattr(m_fd, TCSANOW, &termios) < 0) {
|
||||
wxLogError(wxT("Cannot set the attributes for %s"), m_device.c_str());
|
||||
::close(m_fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_assertRTS) {
|
||||
unsigned int y;
|
||||
if (::ioctl(m_fd, TIOCMGET, &y) < 0) {
|
||||
wxLogError(wxT("Cannot get the control attributes for %s"), m_device.c_str());
|
||||
::close(m_fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
y |= TIOCM_RTS;
|
||||
|
||||
if (::ioctl(m_fd, TIOCMSET, &y) < 0) {
|
||||
wxLogError(wxT("Cannot set the control attributes for %s"), m_device.c_str());
|
||||
::close(m_fd);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int CSerialDataController::read(unsigned char* buffer, unsigned int length)
|
||||
{
|
||||
wxASSERT(buffer != NULL);
|
||||
wxASSERT(m_fd != -1);
|
||||
|
||||
if (length == 0U)
|
||||
return 0;
|
||||
|
||||
unsigned int offset = 0U;
|
||||
|
||||
while (offset < length) {
|
||||
fd_set fds;
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(m_fd, &fds);
|
||||
|
||||
int n;
|
||||
if (offset == 0U) {
|
||||
struct timeval tv;
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 0;
|
||||
|
||||
n = ::select(m_fd + 1, &fds, NULL, NULL, &tv);
|
||||
if (n == 0)
|
||||
return 0;
|
||||
} else {
|
||||
n = ::select(m_fd + 1, &fds, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
if (n < 0) {
|
||||
wxLogError(wxT("Error from select(), errno=%d"), errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (n > 0) {
|
||||
ssize_t len = ::read(m_fd, buffer + offset, length - offset);
|
||||
if (len < 0) {
|
||||
if (errno != EAGAIN) {
|
||||
wxLogError(wxT("Error from read(), errno=%d"), errno);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (len > 0)
|
||||
offset += len;
|
||||
}
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
int CSerialDataController::write(const unsigned char* buffer, unsigned int length)
|
||||
{
|
||||
wxASSERT(buffer != NULL);
|
||||
wxASSERT(m_fd != -1);
|
||||
|
||||
if (length == 0U)
|
||||
return 0;
|
||||
|
||||
unsigned int ptr = 0U;
|
||||
|
||||
while (ptr < length) {
|
||||
ssize_t n = ::write(m_fd, buffer + ptr, length - ptr);
|
||||
if (n < 0) {
|
||||
if (errno != EAGAIN) {
|
||||
wxLogError(wxT("Error returned from write(), errno=%d"), errno);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (n > 0)
|
||||
ptr += n;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
void CSerialDataController::close()
|
||||
{
|
||||
wxASSERT(m_fd != -1);
|
||||
|
||||
::close(m_fd);
|
||||
m_fd = -1;
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (C) 2002-2004,2007-2009,2011-2013,2015 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 1999-2001 by Thomas Sailor HB9JNX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef SerialDataController_H
|
||||
#define SerialDataController_H
|
||||
|
||||
#include <wx/wx.h>
|
||||
|
||||
#if defined(__WINDOWS__)
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
enum SERIAL_SPEED {
|
||||
SERIAL_1200 = 1200,
|
||||
SERIAL_2400 = 2400,
|
||||
SERIAL_4800 = 4800,
|
||||
SERIAL_9600 = 9600,
|
||||
SERIAL_19200 = 19200,
|
||||
SERIAL_38400 = 38400,
|
||||
SERIAL_76800 = 76800,
|
||||
SERIAL_115200 = 115200,
|
||||
SERIAL_230400 = 230400
|
||||
};
|
||||
|
||||
class CSerialDataController {
|
||||
public:
|
||||
CSerialDataController(const wxString& device, SERIAL_SPEED speed, bool assertRTS = false);
|
||||
~CSerialDataController();
|
||||
|
||||
bool open();
|
||||
|
||||
int read(unsigned char* buffer, unsigned int length);
|
||||
int write(const unsigned char* buffer, unsigned int length);
|
||||
|
||||
void close();
|
||||
|
||||
private:
|
||||
wxString m_device;
|
||||
SERIAL_SPEED m_speed;
|
||||
bool m_assertRTS;
|
||||
#if defined(__WINDOWS__)
|
||||
HANDLE m_handle;
|
||||
OVERLAPPED m_readOverlapped;
|
||||
OVERLAPPED m_writeOverlapped;
|
||||
unsigned char* m_readBuffer;
|
||||
unsigned int m_readLength;
|
||||
bool m_readPending;
|
||||
#else
|
||||
int m_fd;
|
||||
#endif
|
||||
|
||||
#if defined(__WINDOWS__)
|
||||
int readNonblock(unsigned char* buffer, unsigned int length);
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,533 @@
|
||||
/*
|
||||
* Copyright (C) 2002-2004,2007-2011,2013,2015 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 1999-2001 by Thomas Sailor HB9JNX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "SerialLineController.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#if defined(__WINDOWS__)
|
||||
#include <setupapi.h>
|
||||
#include <winioctl.h>
|
||||
#else
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <cerrno>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <termios.h>
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(__WINDOWS__)
|
||||
|
||||
CSerialLineController::CSerialLineController(const wxString& device, unsigned int config) :
|
||||
m_device(device),
|
||||
m_config(config),
|
||||
m_rts(false),
|
||||
m_dtr(false),
|
||||
m_handle(INVALID_HANDLE_VALUE)
|
||||
{
|
||||
wxASSERT(!device.IsEmpty());
|
||||
wxASSERT(config == 1U || config == 2U || config == 3U);
|
||||
}
|
||||
|
||||
CSerialLineController::~CSerialLineController()
|
||||
{
|
||||
}
|
||||
|
||||
bool CSerialLineController::open()
|
||||
{
|
||||
wxASSERT(m_handle == INVALID_HANDLE_VALUE);
|
||||
|
||||
DWORD errCode;
|
||||
|
||||
m_handle = ::CreateFile(m_device.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
|
||||
if (m_handle == INVALID_HANDLE_VALUE) {
|
||||
wxLogError(wxT("Cannot open device - %s"), m_device.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
DCB dcb;
|
||||
if (::GetCommState(m_handle, &dcb) == 0) {
|
||||
wxLogError(wxT("Cannot get the attributes for %s"), m_device.c_str());
|
||||
::ClearCommError(m_handle, &errCode, NULL);
|
||||
::CloseHandle(m_handle);
|
||||
return false;
|
||||
}
|
||||
|
||||
dcb.fOutxCtsFlow = FALSE;
|
||||
dcb.fOutxDsrFlow = FALSE;
|
||||
dcb.fDtrControl = DTR_CONTROL_DISABLE;
|
||||
dcb.fRtsControl = RTS_CONTROL_DISABLE;
|
||||
|
||||
if (::SetCommState(m_handle, &dcb) == 0) {
|
||||
wxLogError(wxT("Cannot set the attributes for %s"), m_device.c_str());
|
||||
::ClearCommError(m_handle, &errCode, NULL);
|
||||
::CloseHandle(m_handle);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (::EscapeCommFunction(m_handle, CLRDTR) == 0) {
|
||||
wxLogError(wxT("Cannot clear DTR for %s"), m_device.c_str());
|
||||
::ClearCommError(m_handle, &errCode, NULL);
|
||||
::CloseHandle(m_handle);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (::EscapeCommFunction(m_handle, CLRRTS) == 0) {
|
||||
wxLogError(wxT("Cannot clear RTS for %s"), m_device.c_str());
|
||||
::ClearCommError(m_handle, &errCode, NULL);
|
||||
::CloseHandle(m_handle);
|
||||
return false;
|
||||
}
|
||||
|
||||
::ClearCommError(m_handle, &errCode, NULL);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CSerialLineController::getCD() const
|
||||
{
|
||||
wxASSERT(m_handle != INVALID_HANDLE_VALUE);
|
||||
|
||||
DWORD status;
|
||||
DWORD errCode;
|
||||
if (::GetCommModemStatus(m_handle, &status) == 0) {
|
||||
::ClearCommError(m_handle, &errCode, NULL);
|
||||
return false;
|
||||
}
|
||||
|
||||
return (status & MS_RLSD_ON) == MS_RLSD_ON;
|
||||
}
|
||||
|
||||
bool CSerialLineController::getCTS() const
|
||||
{
|
||||
wxASSERT(m_handle != INVALID_HANDLE_VALUE);
|
||||
|
||||
DWORD status;
|
||||
DWORD errCode;
|
||||
if (::GetCommModemStatus(m_handle, &status) == 0) {
|
||||
::ClearCommError(m_handle, &errCode, NULL);
|
||||
return false;
|
||||
}
|
||||
|
||||
return (status & MS_CTS_ON) == MS_CTS_ON;
|
||||
}
|
||||
|
||||
bool CSerialLineController::getDSR() const
|
||||
{
|
||||
wxASSERT(m_handle != INVALID_HANDLE_VALUE);
|
||||
|
||||
DWORD status;
|
||||
DWORD errCode;
|
||||
if (::GetCommModemStatus(m_handle, &status) == 0) {
|
||||
::ClearCommError(m_handle, &errCode, NULL);
|
||||
return false;
|
||||
}
|
||||
|
||||
return (status & MS_DSR_ON) == MS_DSR_ON;
|
||||
}
|
||||
|
||||
bool CSerialLineController::setRTS(bool set)
|
||||
{
|
||||
wxASSERT(m_handle != INVALID_HANDLE_VALUE);
|
||||
|
||||
if (set == m_rts)
|
||||
return true;
|
||||
|
||||
DWORD rts = (set) ? SETRTS : CLRRTS;
|
||||
DWORD errCode;
|
||||
|
||||
if (::EscapeCommFunction(m_handle, rts) == 0) {
|
||||
::ClearCommError(m_handle, &errCode, NULL);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_rts = set;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CSerialLineController::setDTR(bool set)
|
||||
{
|
||||
wxASSERT(m_handle != INVALID_HANDLE_VALUE);
|
||||
|
||||
if (set == m_dtr)
|
||||
return true;
|
||||
|
||||
DWORD dtr = (set) ? SETDTR : CLRDTR;
|
||||
DWORD errCode;
|
||||
|
||||
if (::EscapeCommFunction(m_handle, dtr) == 0) {
|
||||
::ClearCommError(m_handle, &errCode, NULL);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_dtr = set;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CSerialLineController::getDigitalInputs(bool& inp1, bool& inp2, bool& inp3, bool& inp4, bool& inp5)
|
||||
{
|
||||
wxASSERT(m_handle != INVALID_HANDLE_VALUE);
|
||||
|
||||
inp1 = inp2 = inp3 = inp4 = inp5 = false;
|
||||
|
||||
DWORD status;
|
||||
DWORD errCode;
|
||||
if (::GetCommModemStatus(m_handle, &status) == 0) {
|
||||
::ClearCommError(m_handle, &errCode, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (m_config) {
|
||||
case 1U:
|
||||
inp1 = (status & MS_DSR_ON) == MS_DSR_ON;
|
||||
inp2 = (status & MS_CTS_ON) == MS_CTS_ON;
|
||||
break;
|
||||
case 2U:
|
||||
inp1 = (status & MS_RLSD_ON) == MS_RLSD_ON;
|
||||
inp2 = (status & MS_RLSD_ON) == MS_RLSD_ON;
|
||||
break;
|
||||
case 3U:
|
||||
inp1 = (status & MS_RLSD_ON) == MS_RLSD_ON;
|
||||
break;
|
||||
default:
|
||||
wxLogError(wxT("Unknown serial config - %u"), m_config);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CSerialLineController::setDigitalOutputs(bool outp1, bool, bool outp3, bool, bool, bool, bool, bool)
|
||||
{
|
||||
wxASSERT(m_handle != INVALID_HANDLE_VALUE);
|
||||
|
||||
switch (m_config) {
|
||||
case 1U:
|
||||
if (outp1 != m_dtr) {
|
||||
DWORD dtr = (outp1) ? SETDTR : CLRDTR;
|
||||
DWORD errCode;
|
||||
|
||||
if (::EscapeCommFunction(m_handle, dtr) == 0) {
|
||||
::ClearCommError(m_handle, &errCode, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
m_dtr = outp1;
|
||||
}
|
||||
if (outp3 != m_rts) {
|
||||
DWORD rts = (outp3) ? SETRTS : CLRRTS;
|
||||
DWORD errCode;
|
||||
|
||||
if (::EscapeCommFunction(m_handle, rts) == 0) {
|
||||
::ClearCommError(m_handle, &errCode, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
m_rts = outp3;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2U:
|
||||
case 3U:
|
||||
if (outp1 != m_rts) {
|
||||
DWORD rts = (outp1) ? SETRTS : CLRRTS;
|
||||
DWORD errCode;
|
||||
|
||||
if (::EscapeCommFunction(m_handle, rts) == 0) {
|
||||
::ClearCommError(m_handle, &errCode, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
m_rts = outp1;
|
||||
}
|
||||
if (outp3 != m_dtr) {
|
||||
DWORD dtr = (outp3) ? SETDTR : CLRDTR;
|
||||
DWORD errCode;
|
||||
|
||||
if (::EscapeCommFunction(m_handle, dtr) == 0) {
|
||||
::ClearCommError(m_handle, &errCode, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
m_dtr = outp3;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
wxLogError(wxT("Unknown serial config - %u"), m_config);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CSerialLineController::close()
|
||||
{
|
||||
wxASSERT(m_handle != INVALID_HANDLE_VALUE);
|
||||
|
||||
::CloseHandle(m_handle);
|
||||
m_handle = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
CSerialLineController::CSerialLineController(const wxString& device, unsigned int config) :
|
||||
m_device(device),
|
||||
m_config(config),
|
||||
m_rts(false),
|
||||
m_dtr(false),
|
||||
m_fd(-1)
|
||||
{
|
||||
wxASSERT(!device.IsEmpty());
|
||||
wxASSERT(config == 1U || config == 2U || config == 3U);
|
||||
}
|
||||
|
||||
CSerialLineController::~CSerialLineController()
|
||||
{
|
||||
}
|
||||
|
||||
bool CSerialLineController::open()
|
||||
{
|
||||
wxASSERT(m_fd == -1);
|
||||
|
||||
m_fd = ::open(m_device.mb_str(), O_RDWR | O_NOCTTY | O_NDELAY, 0);
|
||||
if (m_fd < 0) {
|
||||
wxLogError(wxT("Cannot open device - %s"), m_device.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (::isatty(m_fd) == 0) {
|
||||
wxLogError(wxT("%s is not a TTY device"), m_device.c_str());
|
||||
::close(m_fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
termios termios;
|
||||
if (::tcgetattr(m_fd, &termios) < 0) {
|
||||
wxLogError(wxT("Cannot get the attributes for %s"), m_device.c_str());
|
||||
::close(m_fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
// FIXME XXX unfinished
|
||||
termios.c_cflag |= (CLOCAL | CREAD);
|
||||
termios.c_cflag &= ~CRTSCTS;
|
||||
termios.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
|
||||
termios.c_oflag &= ~OPOST;
|
||||
termios.c_cc[VMIN] = 0;
|
||||
termios.c_cc[VTIME] = 10;
|
||||
|
||||
if (::tcsetattr(m_fd, TCSANOW, &termios) < 0) {
|
||||
wxLogError(wxT("Cannot set the attributes for %s"), m_device.c_str());
|
||||
::close(m_fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned int y;
|
||||
if (::ioctl(m_fd, TIOCMGET, &y) < 0) {
|
||||
wxLogError(wxT("Cannot get the modem status bits for %s"), m_device.c_str());
|
||||
::close(m_fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
y &= ~TIOCM_DTR;
|
||||
y &= ~TIOCM_RTS;
|
||||
|
||||
if (::ioctl(m_fd, TIOCMSET, &y) < 0) {
|
||||
wxLogError(wxT("Cannot set the modem status bits for %s"), m_device.c_str());
|
||||
::close(m_fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CSerialLineController::getCD() const
|
||||
{
|
||||
wxASSERT(m_fd != -1);
|
||||
|
||||
unsigned int y;
|
||||
if (::ioctl(m_fd, TIOCMGET, &y) < 0)
|
||||
return false;
|
||||
|
||||
return (y & TIOCM_CD) == TIOCM_CD;
|
||||
}
|
||||
|
||||
bool CSerialLineController::getCTS() const
|
||||
{
|
||||
wxASSERT(m_fd != -1);
|
||||
|
||||
unsigned int y;
|
||||
if (::ioctl(m_fd, TIOCMGET, &y) < 0)
|
||||
return false;
|
||||
|
||||
return (y & TIOCM_CTS) == TIOCM_CTS;
|
||||
}
|
||||
|
||||
bool CSerialLineController::getDSR() const
|
||||
{
|
||||
wxASSERT(m_fd != -1);
|
||||
|
||||
unsigned int y;
|
||||
if (::ioctl(m_fd, TIOCMGET, &y) < 0)
|
||||
return false;
|
||||
|
||||
return (y & TIOCM_DSR) == TIOCM_DSR;
|
||||
}
|
||||
|
||||
bool CSerialLineController::setRTS(bool set)
|
||||
{
|
||||
wxASSERT(m_fd != -1);
|
||||
|
||||
if (set == m_rts)
|
||||
return true;
|
||||
|
||||
unsigned int y;
|
||||
if (::ioctl(m_fd, TIOCMGET, &y) < 0)
|
||||
return false;
|
||||
|
||||
if (set)
|
||||
y |= TIOCM_RTS;
|
||||
else
|
||||
y &= ~TIOCM_RTS;
|
||||
|
||||
if (::ioctl(m_fd, TIOCMSET, &y) < 0)
|
||||
return false;
|
||||
|
||||
m_rts = set;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CSerialLineController::setDTR(bool set)
|
||||
{
|
||||
wxASSERT(m_fd != -1);
|
||||
|
||||
if (set == m_dtr)
|
||||
return true;
|
||||
|
||||
unsigned int y;
|
||||
if (::ioctl(m_fd, TIOCMGET, &y) < 0)
|
||||
return false;
|
||||
|
||||
if (set)
|
||||
y |= TIOCM_DTR;
|
||||
else
|
||||
y &= ~TIOCM_DTR;
|
||||
|
||||
if (::ioctl(m_fd, TIOCMSET, &y) < 0)
|
||||
return false;
|
||||
|
||||
m_dtr = set;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CSerialLineController::getDigitalInputs(bool& inp1, bool& inp2, bool& inp3, bool& inp4, bool& inp5)
|
||||
{
|
||||
wxASSERT(m_fd != -1);
|
||||
|
||||
inp1 = inp2 = inp3 = inp4 = inp5 = false;
|
||||
|
||||
unsigned int y;
|
||||
if (::ioctl(m_fd, TIOCMGET, &y) < 0)
|
||||
return;
|
||||
|
||||
switch (m_config) {
|
||||
case 1U:
|
||||
inp1 = (y & TIOCM_DSR) == TIOCM_DSR;
|
||||
inp2 = (y & TIOCM_CTS) == TIOCM_CTS;
|
||||
break;
|
||||
case 2U:
|
||||
inp1 = (y & TIOCM_CD) == TIOCM_CD;
|
||||
inp2 = (y & TIOCM_CD) == TIOCM_CD;
|
||||
break;
|
||||
case 3U:
|
||||
inp1 = (y & TIOCM_CD) == TIOCM_CD;
|
||||
break;
|
||||
default:
|
||||
wxLogError(wxT("Unknown serial config - %u"), m_config);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CSerialLineController::setDigitalOutputs(bool outp1, bool, bool outp3, bool, bool, bool, bool, bool)
|
||||
{
|
||||
wxASSERT(m_fd != -1);
|
||||
|
||||
if (m_config == 1U) {
|
||||
if (outp1 == m_dtr && outp3 == m_rts)
|
||||
return;
|
||||
|
||||
unsigned int y;
|
||||
if (::ioctl(m_fd, TIOCMGET, &y) < 0)
|
||||
return;
|
||||
|
||||
if (outp1)
|
||||
y |= TIOCM_DTR;
|
||||
else
|
||||
y &= ~TIOCM_DTR;
|
||||
|
||||
if (outp3)
|
||||
y |= TIOCM_RTS;
|
||||
else
|
||||
y &= ~TIOCM_RTS;
|
||||
|
||||
if (::ioctl(m_fd, TIOCMSET, &y) < 0)
|
||||
return;
|
||||
|
||||
m_dtr = outp1;
|
||||
m_rts = outp3;
|
||||
} else if (m_config == 2U || m_config == 3U) {
|
||||
if (outp1 == m_rts && outp3 == m_dtr)
|
||||
return;
|
||||
|
||||
unsigned int y;
|
||||
if (::ioctl(m_fd, TIOCMGET, &y) < 0)
|
||||
return;
|
||||
|
||||
if (outp1)
|
||||
y |= TIOCM_RTS;
|
||||
else
|
||||
y &= ~TIOCM_RTS;
|
||||
|
||||
if (outp3)
|
||||
y |= TIOCM_DTR;
|
||||
else
|
||||
y &= ~TIOCM_DTR;
|
||||
|
||||
if (::ioctl(m_fd, TIOCMSET, &y) < 0)
|
||||
return;
|
||||
|
||||
m_rts = outp1;
|
||||
m_dtr = outp3;
|
||||
} else {
|
||||
wxLogError(wxT("Unknown serial config - %u"), m_config);
|
||||
}
|
||||
}
|
||||
|
||||
void CSerialLineController::close()
|
||||
{
|
||||
wxASSERT(m_fd != -1);
|
||||
|
||||
::close(m_fd);
|
||||
m_fd = -1;
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (C) 2002-2004,2007-2009,2011-2013,2015 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 1999-2001 by Thomas Sailor HB9JNX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef SerialLineController_H
|
||||
#define SerialLineController_H
|
||||
|
||||
#include "HardwareController.h"
|
||||
|
||||
#include <wx/wx.h>
|
||||
|
||||
#if defined(__WINDOWS__)
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
enum SERIALPIN {
|
||||
SERIAL_CD,
|
||||
SERIAL_CTS,
|
||||
SERIAL_DSR,
|
||||
SERIAL_DTR,
|
||||
SERIAL_RTS,
|
||||
SERIAL_ECHOLINK
|
||||
};
|
||||
|
||||
const unsigned int MAX_DEVICE_NAME = 255U;
|
||||
|
||||
class CSerialLineController : public IHardwareController {
|
||||
public:
|
||||
CSerialLineController(const wxString& device, unsigned int config = 1U);
|
||||
virtual ~CSerialLineController();
|
||||
|
||||
virtual bool open();
|
||||
|
||||
virtual bool setRTS(bool set);
|
||||
virtual bool setDTR(bool set);
|
||||
|
||||
virtual bool getCD() const;
|
||||
virtual bool getCTS() const;
|
||||
virtual bool getDSR() const;
|
||||
|
||||
virtual void getDigitalInputs(bool& inp1, bool& inp2, bool& inp3, bool& inp4, bool& inp5);
|
||||
virtual void setDigitalOutputs(bool outp1, bool outp2, bool outp3, bool outp4, bool outp5, bool outp6, bool outp7, bool outp8);
|
||||
|
||||
virtual void close();
|
||||
|
||||
private:
|
||||
wxString m_device;
|
||||
unsigned int m_config;
|
||||
bool m_rts;
|
||||
bool m_dtr;
|
||||
#if defined(__WINDOWS__)
|
||||
HANDLE m_handle;
|
||||
#else
|
||||
int m_fd;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Copyright (C) 2002-2004,2007-2011,2013,2015 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "SerialPortSelector.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <IOKit/IOTypes.h>
|
||||
#include <IOKit/IOKitLib.h>
|
||||
#include <IOKit/serial/IOSerialKeys.h>
|
||||
#include <IOKit/IOBSD.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <termios.h>
|
||||
#elif defined(__WINDOWS__)
|
||||
#include <setupapi.h>
|
||||
#include <winioctl.h>
|
||||
#else
|
||||
#include <wx/dir.h>
|
||||
#endif
|
||||
|
||||
|
||||
wxArrayString CSerialPortSelector::getDevices()
|
||||
{
|
||||
wxArrayString devices;
|
||||
|
||||
devices.Alloc(10);
|
||||
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
mach_port_t masterPort;
|
||||
kern_return_t ret = ::IOMasterPort(MACH_PORT_NULL, &masterPort);
|
||||
if (ret != KERN_SUCCESS)
|
||||
return devices;
|
||||
|
||||
CFMutableDictionaryRef match = ::IOServiceMatching(kIOSerialBSDServiceValue);
|
||||
if (match == NULL)
|
||||
wxLogWarning(wxT("IOServiceMatching() returned NULL"));
|
||||
else
|
||||
::CFDictionarySetValue(match, CFSTR(kIOSerialBSDTypeKey), CFSTR(kIOSerialBSDRS232Type));
|
||||
|
||||
io_iterator_t services;
|
||||
ret = ::IOServiceGetMatchingServices(masterPort, match, &services);
|
||||
if (ret != KERN_SUCCESS)
|
||||
return devices;
|
||||
|
||||
io_object_t modem;
|
||||
while ((modem = ::IOIteratorNext(services))) {
|
||||
CFTypeRef filePath = ::IORegistryEntryCreateCFProperty(modem, CFSTR(kIOCalloutDeviceKey), kCFAllocatorDefault, 0);
|
||||
if (filePath != NULL) {
|
||||
char port[MAXPATHLEN];
|
||||
Boolean result = ::CFStringGetCString((const __CFString*)filePath, port, MAXPATHLEN, kCFStringEncodingASCII);
|
||||
::CFRelease(filePath);
|
||||
|
||||
if (result)
|
||||
devices.Add(port);
|
||||
|
||||
::IOObjectRelease(modem);
|
||||
}
|
||||
}
|
||||
|
||||
::IOObjectRelease(services);
|
||||
#elif defined(__WINDOWS__)
|
||||
devices.Add(wxT("\\\\.\\COM1"));
|
||||
devices.Add(wxT("\\\\.\\COM2"));
|
||||
devices.Add(wxT("\\\\.\\COM3"));
|
||||
devices.Add(wxT("\\\\.\\COM4"));
|
||||
devices.Add(wxT("\\\\.\\COM5"));
|
||||
devices.Add(wxT("\\\\.\\COM6"));
|
||||
devices.Add(wxT("\\\\.\\COM7"));
|
||||
devices.Add(wxT("\\\\.\\COM8"));
|
||||
devices.Add(wxT("\\\\.\\COM9"));
|
||||
devices.Add(wxT("\\\\.\\COM10"));
|
||||
devices.Add(wxT("\\\\.\\COM11"));
|
||||
devices.Add(wxT("\\\\.\\COM12"));
|
||||
devices.Add(wxT("\\\\.\\COM13"));
|
||||
devices.Add(wxT("\\\\.\\COM14"));
|
||||
devices.Add(wxT("\\\\.\\COM15"));
|
||||
devices.Add(wxT("\\\\.\\COM16"));
|
||||
devices.Add(wxT("\\\\.\\COM17"));
|
||||
devices.Add(wxT("\\\\.\\COM18"));
|
||||
devices.Add(wxT("\\\\.\\COM19"));
|
||||
#else
|
||||
wxDir devDir;
|
||||
bool ret = devDir.Open(wxT("/dev"));
|
||||
|
||||
if (ret) {
|
||||
wxString fileName;
|
||||
ret = devDir.GetFirst(&fileName, wxT("ttyACM*"), wxDIR_FILES);
|
||||
|
||||
while (ret) {
|
||||
fileName.Prepend(wxT("/dev/"));
|
||||
devices.Add(fileName);
|
||||
|
||||
ret = devDir.GetNext(&fileName);
|
||||
}
|
||||
|
||||
ret = devDir.GetFirst(&fileName, wxT("ttyAMA*"), wxDIR_FILES);
|
||||
|
||||
while (ret) {
|
||||
fileName.Prepend(wxT("/dev/"));
|
||||
devices.Add(fileName);
|
||||
|
||||
ret = devDir.GetNext(&fileName);
|
||||
}
|
||||
|
||||
ret = devDir.GetFirst(&fileName, wxT("ttyS*"), wxDIR_FILES);
|
||||
|
||||
while (ret) {
|
||||
fileName.Prepend(wxT("/dev/"));
|
||||
devices.Add(fileName);
|
||||
|
||||
ret = devDir.GetNext(&fileName);
|
||||
}
|
||||
|
||||
ret = devDir.GetFirst(&fileName, wxT("ttyUSB*"), wxDIR_FILES);
|
||||
|
||||
while (ret) {
|
||||
fileName.Prepend(wxT("/dev/"));
|
||||
devices.Add(fileName);
|
||||
|
||||
ret = devDir.GetNext(&fileName);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return devices;
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C) 2002-2004,2007-2009,2011-2013,2015 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef SerialPortSelector_H
|
||||
#define SerialPortSelector_H
|
||||
|
||||
#include <wx/wx.h>
|
||||
|
||||
class CSerialPortSelector {
|
||||
public:
|
||||
static wxArrayString getDevices();
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Copyright (C) 2009,2010,2011 by Jonathan Naylor, G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "CCITTChecksumReverse.h"
|
||||
#include "SlowDataDecoder.h"
|
||||
#include "DStarDefines.h"
|
||||
|
||||
const unsigned int SLOW_DATA_BLOCK_SIZE = 6U;
|
||||
|
||||
const unsigned int SLOW_DATA_FULL_BLOCK_SIZE = (SLOW_DATA_BLOCK_SIZE - 1U) * 10U;
|
||||
|
||||
CSlowDataDecoder::CSlowDataDecoder() :
|
||||
m_buffer(NULL),
|
||||
m_state(SDD_FIRST),
|
||||
m_headerData(NULL),
|
||||
m_headerPtr(0U),
|
||||
m_header(NULL)
|
||||
{
|
||||
m_buffer = new unsigned char[SLOW_DATA_BLOCK_SIZE];
|
||||
m_headerData = new unsigned char[SLOW_DATA_FULL_BLOCK_SIZE];
|
||||
}
|
||||
|
||||
CSlowDataDecoder::~CSlowDataDecoder()
|
||||
{
|
||||
delete[] m_buffer;
|
||||
delete[] m_headerData;
|
||||
|
||||
delete m_header;
|
||||
}
|
||||
|
||||
void CSlowDataDecoder::addData(const unsigned char* data)
|
||||
{
|
||||
wxASSERT(data != NULL);
|
||||
|
||||
switch (m_state) {
|
||||
case SDD_FIRST:
|
||||
m_buffer[0] = data[0] ^ SCRAMBLER_BYTE1;
|
||||
m_buffer[1] = data[1] ^ SCRAMBLER_BYTE2;
|
||||
m_buffer[2] = data[2] ^ SCRAMBLER_BYTE3;
|
||||
m_state = SDD_SECOND;
|
||||
return;
|
||||
|
||||
case SDD_SECOND:
|
||||
m_buffer[3] = data[0] ^ SCRAMBLER_BYTE1;
|
||||
m_buffer[4] = data[1] ^ SCRAMBLER_BYTE2;
|
||||
m_buffer[5] = data[2] ^ SCRAMBLER_BYTE3;
|
||||
m_state = SDD_FIRST;
|
||||
processHeader();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CHeaderData* CSlowDataDecoder::getHeaderData()
|
||||
{
|
||||
if (m_header == NULL)
|
||||
return NULL;
|
||||
|
||||
CHeaderData* temp = new CHeaderData(*m_header);
|
||||
|
||||
reset();
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
void CSlowDataDecoder::sync()
|
||||
{
|
||||
m_headerPtr = 0U;
|
||||
|
||||
m_state = SDD_FIRST;
|
||||
}
|
||||
|
||||
void CSlowDataDecoder::reset()
|
||||
{
|
||||
m_headerPtr = 0U;
|
||||
|
||||
m_state = SDD_FIRST;
|
||||
|
||||
::memset(m_headerData, 0x00, SLOW_DATA_FULL_BLOCK_SIZE * sizeof(unsigned char));
|
||||
|
||||
delete m_header;
|
||||
m_header = NULL;
|
||||
}
|
||||
|
||||
void CSlowDataDecoder::processHeader()
|
||||
{
|
||||
// Do we have a complete and valid header already?
|
||||
if (m_header != NULL)
|
||||
return;
|
||||
|
||||
for (unsigned int i = 1U; i <= (SLOW_DATA_BLOCK_SIZE - 1U); i++, m_headerPtr++) {
|
||||
m_headerData[m_headerPtr] = m_buffer[i];
|
||||
|
||||
if (m_headerPtr >= SLOW_DATA_FULL_BLOCK_SIZE)
|
||||
m_headerPtr = 0U;
|
||||
}
|
||||
|
||||
// Look for a header starting immediately after the data sync
|
||||
bool res = processHeader(m_headerData);
|
||||
if (res)
|
||||
return;
|
||||
|
||||
// Look for a header starting one block after the data sync
|
||||
processHeader(m_headerData + SLOW_DATA_BLOCK_SIZE - 1U);
|
||||
}
|
||||
|
||||
bool CSlowDataDecoder::processHeader(const unsigned char* bytes)
|
||||
{
|
||||
unsigned char buffer[RADIO_HEADER_LENGTH_BYTES];
|
||||
|
||||
// Clean up the header data
|
||||
for (unsigned int n = 0U; n < RADIO_HEADER_LENGTH_BYTES; n++) {
|
||||
switch (n) {
|
||||
case 0U: // Flag 1
|
||||
// Only allow BK and EMR bits and always assert the repeater bit
|
||||
buffer[n] = (bytes[n] & (INTERRUPTED_MASK | URGENT_MASK)) | REPEATER_MASK;
|
||||
break;
|
||||
case 1U: // Flag 2
|
||||
case 2U: // Flag 3
|
||||
// Always 0x00 in current implementations
|
||||
buffer[n] = 0x00;
|
||||
break;
|
||||
case 39U: // Checksum 1
|
||||
case 40U: // Checksum 2
|
||||
// These bytes can be any value
|
||||
buffer[n] = bytes[n];
|
||||
break;
|
||||
default: // Callsign text
|
||||
// Only ASCII text so MSB is always clear
|
||||
buffer[n] = bytes[n] & 0x7F;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CCCITTChecksumReverse cksum;
|
||||
cksum.update(buffer, RADIO_HEADER_LENGTH_BYTES - 2U);
|
||||
|
||||
bool valid = cksum.check(buffer + RADIO_HEADER_LENGTH_BYTES - 2U);
|
||||
if (!valid)
|
||||
return false;
|
||||
|
||||
// The checksum has already been checked
|
||||
m_header = new CHeaderData(buffer, RADIO_HEADER_LENGTH_BYTES, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (C) 2009,2010 by Jonathan Naylor, G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef SlowDataDecoder_H
|
||||
#define SlowDataDecoder_H
|
||||
|
||||
#include "HeaderData.h"
|
||||
|
||||
#include <wx/wx.h>
|
||||
|
||||
enum SDD_STATE {
|
||||
SDD_FIRST,
|
||||
SDD_SECOND
|
||||
};
|
||||
|
||||
class CSlowDataDecoder {
|
||||
public:
|
||||
CSlowDataDecoder();
|
||||
~CSlowDataDecoder();
|
||||
|
||||
void addData(const unsigned char* data);
|
||||
|
||||
CHeaderData* getHeaderData();
|
||||
|
||||
void sync();
|
||||
void reset();
|
||||
|
||||
private:
|
||||
unsigned char* m_buffer;
|
||||
SDD_STATE m_state;
|
||||
unsigned char* m_headerData;
|
||||
unsigned int m_headerPtr;
|
||||
CHeaderData* m_header;
|
||||
|
||||
void processHeader();
|
||||
bool processHeader(const unsigned char* bytes);
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,197 @@
|
||||
/*
|
||||
* Copyright (C) 2010,2011,2015 by Jonathan Naylor, G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "CCITTChecksumReverse.h"
|
||||
#include "SlowDataEncoder.h"
|
||||
#include "DStarDefines.h"
|
||||
|
||||
const unsigned int SLOW_DATA_BLOCK_SIZE = 6U;
|
||||
|
||||
const unsigned int SLOW_DATA_FULL_BLOCK_SIZE = SLOW_DATA_BLOCK_SIZE * 10U;
|
||||
|
||||
CSlowDataEncoder::CSlowDataEncoder() :
|
||||
m_headerData(NULL),
|
||||
m_textData(NULL),
|
||||
m_headerPtr(0U),
|
||||
m_textPtr(0U)
|
||||
{
|
||||
m_headerData = new unsigned char[SLOW_DATA_FULL_BLOCK_SIZE];
|
||||
m_textData = new unsigned char[SLOW_DATA_FULL_BLOCK_SIZE];
|
||||
|
||||
::memset(m_headerData, 'f', SLOW_DATA_FULL_BLOCK_SIZE);
|
||||
::memset(m_textData, 'f', SLOW_DATA_FULL_BLOCK_SIZE);
|
||||
}
|
||||
|
||||
CSlowDataEncoder::~CSlowDataEncoder()
|
||||
{
|
||||
delete[] m_headerData;
|
||||
delete[] m_textData;
|
||||
}
|
||||
|
||||
void CSlowDataEncoder::getHeaderData(unsigned char* data)
|
||||
{
|
||||
wxASSERT(data != NULL);
|
||||
|
||||
data[0U] = m_headerData[m_headerPtr++] ^ SCRAMBLER_BYTE1;
|
||||
data[1U] = m_headerData[m_headerPtr++] ^ SCRAMBLER_BYTE2;
|
||||
data[2U] = m_headerData[m_headerPtr++] ^ SCRAMBLER_BYTE3;
|
||||
|
||||
if (m_headerPtr >= SLOW_DATA_FULL_BLOCK_SIZE)
|
||||
m_headerPtr = 0U;
|
||||
}
|
||||
|
||||
void CSlowDataEncoder::getTextData(unsigned char* data)
|
||||
{
|
||||
wxASSERT(data != NULL);
|
||||
|
||||
data[0U] = m_textData[m_textPtr++] ^ SCRAMBLER_BYTE1;
|
||||
data[1U] = m_textData[m_textPtr++] ^ SCRAMBLER_BYTE2;
|
||||
data[2U] = m_textData[m_textPtr++] ^ SCRAMBLER_BYTE3;
|
||||
|
||||
if (m_textPtr >= SLOW_DATA_FULL_BLOCK_SIZE)
|
||||
m_textPtr = 0U;
|
||||
}
|
||||
|
||||
void CSlowDataEncoder::sync()
|
||||
{
|
||||
m_headerPtr = 0U;
|
||||
m_textPtr = 0U;
|
||||
}
|
||||
|
||||
void CSlowDataEncoder::reset()
|
||||
{
|
||||
m_headerPtr = 0U;
|
||||
m_textPtr = 0U;
|
||||
|
||||
::memset(m_headerData, 'f', SLOW_DATA_FULL_BLOCK_SIZE);
|
||||
::memset(m_textData, 'f', SLOW_DATA_FULL_BLOCK_SIZE);
|
||||
}
|
||||
|
||||
void CSlowDataEncoder::setHeaderData(const CHeaderData& header)
|
||||
{
|
||||
::memset(m_headerData, 'f', SLOW_DATA_FULL_BLOCK_SIZE);
|
||||
|
||||
m_headerData[0U] = SLOW_DATA_TYPE_HEADER | 5U;
|
||||
m_headerData[1U] = header.getFlag1();
|
||||
m_headerData[2U] = header.getFlag2();
|
||||
m_headerData[3U] = header.getFlag3();
|
||||
m_headerData[4U] = header.getRptCall2().GetChar(0);
|
||||
m_headerData[5U] = header.getRptCall2().GetChar(1);
|
||||
|
||||
m_headerData[6U] = SLOW_DATA_TYPE_HEADER | 5U;
|
||||
m_headerData[7U] = header.getRptCall2().GetChar(2);
|
||||
m_headerData[8U] = header.getRptCall2().GetChar(3);
|
||||
m_headerData[9U] = header.getRptCall2().GetChar(4);
|
||||
m_headerData[10U] = header.getRptCall2().GetChar(5);
|
||||
m_headerData[11U] = header.getRptCall2().GetChar(6);
|
||||
|
||||
m_headerData[12U] = SLOW_DATA_TYPE_HEADER | 5U;
|
||||
m_headerData[13U] = header.getRptCall2().GetChar(7);
|
||||
m_headerData[14U] = header.getRptCall1().GetChar(0);
|
||||
m_headerData[15U] = header.getRptCall1().GetChar(1);
|
||||
m_headerData[16U] = header.getRptCall1().GetChar(2);
|
||||
m_headerData[17U] = header.getRptCall1().GetChar(3);
|
||||
|
||||
m_headerData[18U] = SLOW_DATA_TYPE_HEADER | 5U;
|
||||
m_headerData[19U] = header.getRptCall1().GetChar(4);
|
||||
m_headerData[20U] = header.getRptCall1().GetChar(5);
|
||||
m_headerData[21U] = header.getRptCall1().GetChar(6);
|
||||
m_headerData[22U] = header.getRptCall1().GetChar(7);
|
||||
m_headerData[23U] = header.getYourCall().GetChar(0);
|
||||
|
||||
m_headerData[24U] = SLOW_DATA_TYPE_HEADER | 5U;
|
||||
m_headerData[25U] = header.getYourCall().GetChar(1);
|
||||
m_headerData[26U] = header.getYourCall().GetChar(2);
|
||||
m_headerData[27U] = header.getYourCall().GetChar(3);
|
||||
m_headerData[28U] = header.getYourCall().GetChar(4);
|
||||
m_headerData[29U] = header.getYourCall().GetChar(5);
|
||||
|
||||
m_headerData[30U] = SLOW_DATA_TYPE_HEADER | 5U;
|
||||
m_headerData[31U] = header.getYourCall().GetChar(6);
|
||||
m_headerData[32U] = header.getYourCall().GetChar(7);
|
||||
m_headerData[33U] = header.getMyCall1().GetChar(0);
|
||||
m_headerData[34U] = header.getMyCall1().GetChar(1);
|
||||
m_headerData[35U] = header.getMyCall1().GetChar(2);
|
||||
|
||||
m_headerData[36U] = SLOW_DATA_TYPE_HEADER | 5U;
|
||||
m_headerData[37U] = header.getMyCall1().GetChar(3);
|
||||
m_headerData[38U] = header.getMyCall1().GetChar(4);
|
||||
m_headerData[39U] = header.getMyCall1().GetChar(5);
|
||||
m_headerData[40U] = header.getMyCall1().GetChar(6);
|
||||
m_headerData[41U] = header.getMyCall1().GetChar(7);
|
||||
|
||||
m_headerData[42U] = SLOW_DATA_TYPE_HEADER | 5U;
|
||||
m_headerData[43U] = header.getMyCall2().GetChar(0);
|
||||
m_headerData[44U] = header.getMyCall2().GetChar(1);
|
||||
m_headerData[45U] = header.getMyCall2().GetChar(2);
|
||||
m_headerData[46U] = header.getMyCall2().GetChar(3);
|
||||
|
||||
CCCITTChecksumReverse cksum;
|
||||
cksum.update(m_headerData + 1U, 5U);
|
||||
cksum.update(m_headerData + 7U, 5U);
|
||||
cksum.update(m_headerData + 13U, 5U);
|
||||
cksum.update(m_headerData + 19U, 5U);
|
||||
cksum.update(m_headerData + 25U, 5U);
|
||||
cksum.update(m_headerData + 31U, 5U);
|
||||
cksum.update(m_headerData + 37U, 5U);
|
||||
cksum.update(m_headerData + 43U, 4U);
|
||||
|
||||
unsigned char checkSum[2U];
|
||||
cksum.result(checkSum);
|
||||
|
||||
m_headerData[47U] = checkSum[0];
|
||||
|
||||
m_headerData[48U] = SLOW_DATA_TYPE_HEADER | 1U;
|
||||
m_headerData[49U] = checkSum[1];
|
||||
|
||||
m_headerPtr = 0U;
|
||||
}
|
||||
|
||||
void CSlowDataEncoder::setTextData(const wxString& text)
|
||||
{
|
||||
if (text.IsEmpty())
|
||||
return;
|
||||
|
||||
::memset(m_textData, 'f', SLOW_DATA_FULL_BLOCK_SIZE);
|
||||
|
||||
m_textData[0U] = SLOW_DATA_TYPE_TEXT | 0U;
|
||||
m_textData[1U] = text.GetChar(0);
|
||||
m_textData[2U] = text.GetChar(1);
|
||||
m_textData[3U] = text.GetChar(2);
|
||||
m_textData[4U] = text.GetChar(3);
|
||||
m_textData[5U] = text.GetChar(4);
|
||||
|
||||
m_textData[6U] = SLOW_DATA_TYPE_TEXT | 1U;
|
||||
m_textData[7U] = text.GetChar(5);
|
||||
m_textData[8U] = text.GetChar(6);
|
||||
m_textData[9U] = text.GetChar(7);
|
||||
m_textData[10U] = text.GetChar(8);
|
||||
m_textData[11U] = text.GetChar(9);
|
||||
|
||||
m_textData[12U] = SLOW_DATA_TYPE_TEXT | 2U;
|
||||
m_textData[13U] = text.GetChar(10);
|
||||
m_textData[14U] = text.GetChar(11);
|
||||
m_textData[15U] = text.GetChar(12);
|
||||
m_textData[16U] = text.GetChar(13);
|
||||
m_textData[17U] = text.GetChar(14);
|
||||
|
||||
m_textData[18U] = SLOW_DATA_TYPE_TEXT | 3U;
|
||||
m_textData[19U] = text.GetChar(15);
|
||||
m_textData[20U] = text.GetChar(16);
|
||||
m_textData[21U] = text.GetChar(17);
|
||||
m_textData[22U] = text.GetChar(18);
|
||||
m_textData[23U] = text.GetChar(19);
|
||||
|
||||
m_textPtr = 0U;
|
||||
}
|
||||
|
||||
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2010 by Jonathan Naylor, G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef SlowDataEncoder_H
|
||||
#define SlowDataEncoder_H
|
||||
|
||||
#include "HeaderData.h"
|
||||
|
||||
#include <wx/wx.h>
|
||||
|
||||
class CSlowDataEncoder {
|
||||
public:
|
||||
CSlowDataEncoder();
|
||||
~CSlowDataEncoder();
|
||||
|
||||
void setHeaderData(const CHeaderData& header);
|
||||
void setTextData(const wxString& text);
|
||||
|
||||
void getHeaderData(unsigned char* data);
|
||||
void getTextData(unsigned char* data);
|
||||
|
||||
void reset();
|
||||
void sync();
|
||||
|
||||
private:
|
||||
unsigned char* m_headerData;
|
||||
unsigned char* m_textData;
|
||||
unsigned int m_headerPtr;
|
||||
unsigned int m_textPtr;
|
||||
};
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2015 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef SoundCardController_H
|
||||
#define SoundCardController_H
|
||||
|
||||
#include "SoundCardReaderWriter.h"
|
||||
#include "DStarGMSKDemodulator.h"
|
||||
#include "DStarGMSKModulator.h"
|
||||
#include "AudioCallback.h"
|
||||
#include "RingBuffer.h"
|
||||
#include "Modem.h"
|
||||
#include "Utils.h"
|
||||
|
||||
#include <wx/wx.h>
|
||||
|
||||
enum DSRSCC_STATE {
|
||||
DSRSCCS_NONE,
|
||||
DSRSCCS_HEADER,
|
||||
DSRSCCS_DATA
|
||||
};
|
||||
|
||||
class CSoundCardController : public CModem, public IAudioCallback {
|
||||
public:
|
||||
CSoundCardController(const wxString& rxDevice, const wxString& txDevice, bool rxInvert, bool txInvert, wxFloat32 rxLevel, wxFloat32 txLevel, unsigned int txDelay, unsigned int txTail);
|
||||
virtual ~CSoundCardController();
|
||||
|
||||
virtual void* Entry();
|
||||
|
||||
virtual bool start();
|
||||
|
||||
virtual unsigned int getSpace();
|
||||
virtual bool isTXReady();
|
||||
virtual bool isTX();
|
||||
|
||||
virtual bool writeHeader(const CHeaderData& header);
|
||||
virtual bool writeData(const unsigned char* data, unsigned int length, bool end);
|
||||
|
||||
virtual void readCallback(const wxFloat32* input, unsigned int n, int id);
|
||||
virtual void writeCallback(wxFloat32* output, unsigned int& n, int id);
|
||||
|
||||
private:
|
||||
CSoundCardReaderWriter m_sound;
|
||||
wxFloat32 m_rxLevel;
|
||||
wxFloat32 m_txLevel;
|
||||
unsigned int m_txDelay;
|
||||
unsigned int m_txTail;
|
||||
CRingBuffer<wxFloat32> m_txAudio;
|
||||
CRingBuffer<wxFloat32> m_rxAudio;
|
||||
DSRSCC_STATE m_rxState;
|
||||
wxUint32 m_patternBuffer;
|
||||
CDStarGMSKDemodulator m_demodulator;
|
||||
CDStarGMSKModulator m_modulator;
|
||||
unsigned char* m_rxBuffer;
|
||||
unsigned int m_rxBufferBits;
|
||||
unsigned int m_dataBits;
|
||||
unsigned int m_mar;
|
||||
int* m_pathMetric;
|
||||
unsigned int* m_pathMemory0;
|
||||
unsigned int* m_pathMemory1;
|
||||
unsigned int* m_pathMemory2;
|
||||
unsigned int* m_pathMemory3;
|
||||
unsigned char* m_fecOutput;
|
||||
|
||||
void processNone(bool bit);
|
||||
void processHeader(bool bit);
|
||||
void processData(bool bit);
|
||||
|
||||
bool rxHeader(unsigned char* in, unsigned char* out);
|
||||
void acs(int* metric);
|
||||
void viterbiDecode(int* data);
|
||||
void traceBack();
|
||||
|
||||
void txHeader(const unsigned char* in, unsigned char* out);
|
||||
void writeBits(unsigned char c);
|
||||
|
||||
unsigned int countBits(wxUint32 num);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@ -0,0 +1,787 @@
|
||||
/*
|
||||
* Copyright (C) 2006-2010,2015 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2014 by John Wiseman, G8BPQ
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "SoundCardReaderWriter.h"
|
||||
|
||||
#if (defined(__APPLE__) && defined(__MACH__)) || defined(__WINDOWS__)
|
||||
|
||||
static int scrwCallback(const void* input, void* output, unsigned long nSamples, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void* userData)
|
||||
{
|
||||
wxASSERT(userData != NULL);
|
||||
|
||||
CSoundCardReaderWriter* object = reinterpret_cast<CSoundCardReaderWriter*>(userData);
|
||||
|
||||
object->callback(static_cast<const wxFloat32*>(input), static_cast<wxFloat32*>(output), nSamples);
|
||||
|
||||
return paContinue;
|
||||
}
|
||||
|
||||
CSoundCardReaderWriter::CSoundCardReaderWriter(const wxString& readDevice, const wxString& writeDevice, unsigned int sampleRate, unsigned int blockSize) :
|
||||
m_readDevice(readDevice),
|
||||
m_writeDevice(writeDevice),
|
||||
m_sampleRate(sampleRate),
|
||||
m_blockSize(blockSize),
|
||||
m_callback(NULL),
|
||||
m_id(-1),
|
||||
m_stream(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
CSoundCardReaderWriter::~CSoundCardReaderWriter()
|
||||
{
|
||||
}
|
||||
|
||||
wxArrayString CSoundCardReaderWriter::getReadDevices()
|
||||
{
|
||||
wxArrayString devices;
|
||||
|
||||
devices.Alloc(10);
|
||||
|
||||
PaError error = ::Pa_Initialize();
|
||||
if (error != paNoError)
|
||||
return devices;
|
||||
|
||||
#if defined(__WINDOWS__)
|
||||
PaHostApiIndex apiIndex = ::Pa_HostApiTypeIdToHostApiIndex(paDirectSound);
|
||||
#elif defined(__APPLE__) && defined(__MACH__)
|
||||
PaHostApiIndex apiIndex = ::Pa_HostApiTypeIdToHostApiIndex(paCoreAudio);
|
||||
#else
|
||||
PaHostApiIndex apiIndex = ::Pa_HostApiTypeIdToHostApiIndex(paALSA);
|
||||
#endif
|
||||
if (apiIndex == paHostApiNotFound) {
|
||||
::Pa_Terminate();
|
||||
return devices;
|
||||
}
|
||||
|
||||
PaDeviceIndex n = ::Pa_GetDeviceCount();
|
||||
if (n <= 0) {
|
||||
::Pa_Terminate();
|
||||
return devices;
|
||||
}
|
||||
|
||||
for (PaDeviceIndex i = 0; i < n; i++) {
|
||||
const PaDeviceInfo* device = ::Pa_GetDeviceInfo(i);
|
||||
|
||||
if (device->hostApi != apiIndex)
|
||||
continue;
|
||||
|
||||
if (device->maxInputChannels > 0) {
|
||||
wxString name(device->name, wxConvLocal);
|
||||
devices.Add(name);
|
||||
}
|
||||
}
|
||||
|
||||
::Pa_Terminate();
|
||||
|
||||
return devices;
|
||||
}
|
||||
|
||||
wxArrayString CSoundCardReaderWriter::getWriteDevices()
|
||||
{
|
||||
wxArrayString devices;
|
||||
|
||||
devices.Alloc(10);
|
||||
|
||||
PaError error = ::Pa_Initialize();
|
||||
if (error != paNoError)
|
||||
return devices;
|
||||
|
||||
#if defined(__WINDOWS__)
|
||||
PaHostApiIndex apiIndex = ::Pa_HostApiTypeIdToHostApiIndex(paDirectSound);
|
||||
#elif defined(__APPLE__) && defined(__MACH__)
|
||||
PaHostApiIndex apiIndex = ::Pa_HostApiTypeIdToHostApiIndex(paCoreAudio);
|
||||
#else
|
||||
PaHostApiIndex apiIndex = ::Pa_HostApiTypeIdToHostApiIndex(paALSA);
|
||||
#endif
|
||||
if (apiIndex == paHostApiNotFound) {
|
||||
::Pa_Terminate();
|
||||
return devices;
|
||||
}
|
||||
|
||||
PaDeviceIndex n = ::Pa_GetDeviceCount();
|
||||
if (n <= 0) {
|
||||
::Pa_Terminate();
|
||||
return devices;
|
||||
}
|
||||
|
||||
for (PaDeviceIndex i = 0; i < n; i++) {
|
||||
const PaDeviceInfo* device = ::Pa_GetDeviceInfo(i);
|
||||
|
||||
if (device->hostApi != apiIndex)
|
||||
continue;
|
||||
|
||||
if (device->maxOutputChannels > 0) {
|
||||
wxString name(device->name, wxConvLocal);
|
||||
devices.Add(name);
|
||||
}
|
||||
}
|
||||
|
||||
::Pa_Terminate();
|
||||
|
||||
return devices;
|
||||
}
|
||||
|
||||
void CSoundCardReaderWriter::setCallback(IAudioCallback* callback, int id)
|
||||
{
|
||||
wxASSERT(callback != NULL);
|
||||
|
||||
m_callback = callback;
|
||||
m_id = id;
|
||||
}
|
||||
|
||||
bool CSoundCardReaderWriter::open()
|
||||
{
|
||||
PaError error = ::Pa_Initialize();
|
||||
if (error != paNoError) {
|
||||
wxLogError(wxT("Cannot initialise PortAudio"));
|
||||
return false;
|
||||
}
|
||||
|
||||
PaStreamParameters* pParamsIn = NULL;
|
||||
PaStreamParameters* pParamsOut = NULL;
|
||||
|
||||
PaStreamParameters paramsIn;
|
||||
PaStreamParameters paramsOut;
|
||||
|
||||
PaDeviceIndex inDev, outDev;
|
||||
bool res = convertNameToDevices(inDev, outDev);
|
||||
if (!res) {
|
||||
wxLogError(wxT("Cannot convert name to device"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (inDev != -1) {
|
||||
const PaDeviceInfo* inInfo = ::Pa_GetDeviceInfo(inDev);
|
||||
if (inInfo == NULL) {
|
||||
wxLogError(wxT("Cannot get device information for the input device"));
|
||||
return false;
|
||||
}
|
||||
|
||||
paramsIn.device = inDev;
|
||||
paramsIn.channelCount = 1;
|
||||
paramsIn.sampleFormat = paFloat32;
|
||||
paramsIn.hostApiSpecificStreamInfo = NULL;
|
||||
paramsIn.suggestedLatency = inInfo->defaultLowInputLatency;
|
||||
|
||||
pParamsIn = ¶msIn;
|
||||
}
|
||||
|
||||
if (outDev != -1) {
|
||||
const PaDeviceInfo* outInfo = ::Pa_GetDeviceInfo(outDev);
|
||||
if (outInfo == NULL) {
|
||||
wxLogError(wxT("Cannot get device information for the output device"));
|
||||
return false;
|
||||
}
|
||||
|
||||
paramsOut.device = outDev;
|
||||
paramsOut.channelCount = 1;
|
||||
paramsOut.sampleFormat = paFloat32;
|
||||
paramsOut.hostApiSpecificStreamInfo = NULL;
|
||||
paramsOut.suggestedLatency = outInfo->defaultLowOutputLatency;
|
||||
|
||||
pParamsOut = ¶msOut;
|
||||
}
|
||||
|
||||
error = ::Pa_OpenStream(&m_stream, pParamsIn, pParamsOut, double(m_sampleRate), m_blockSize, paNoFlag, &scrwCallback, this);
|
||||
if (error != paNoError) {
|
||||
wxLogError(wxT("Cannot open the audios stream(s)"));
|
||||
::Pa_Terminate();
|
||||
return false;
|
||||
}
|
||||
|
||||
error = ::Pa_StartStream(m_stream);
|
||||
if (error != paNoError) {
|
||||
wxLogError(wxT("Cannot start the audio stream(s)"));
|
||||
::Pa_CloseStream(m_stream);
|
||||
m_stream = NULL;
|
||||
|
||||
::Pa_Terminate();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CSoundCardReaderWriter::close()
|
||||
{
|
||||
wxASSERT(m_stream != NULL);
|
||||
|
||||
::Pa_AbortStream(m_stream);
|
||||
|
||||
::Pa_CloseStream(m_stream);
|
||||
|
||||
::Pa_Terminate();
|
||||
}
|
||||
|
||||
void CSoundCardReaderWriter::callback(const wxFloat32* input, wxFloat32* output, unsigned int nSamples)
|
||||
{
|
||||
if (m_callback != NULL) {
|
||||
m_callback->readCallback(input, nSamples, m_id);
|
||||
m_callback->writeCallback(output, nSamples, m_id);
|
||||
}
|
||||
}
|
||||
|
||||
bool CSoundCardReaderWriter::convertNameToDevices(PaDeviceIndex& inDev, PaDeviceIndex& outDev)
|
||||
{
|
||||
inDev = outDev = -1;
|
||||
|
||||
#if defined(__WINDOWS__)
|
||||
PaHostApiIndex apiIndex = ::Pa_HostApiTypeIdToHostApiIndex(paDirectSound);
|
||||
#elif defined(__APPLE__) && defined(__MACH__)
|
||||
PaHostApiIndex apiIndex = ::Pa_HostApiTypeIdToHostApiIndex(paCoreAudio);
|
||||
#else
|
||||
PaHostApiIndex apiIndex = ::Pa_HostApiTypeIdToHostApiIndex(paALSA);
|
||||
#endif
|
||||
if (apiIndex == paHostApiNotFound)
|
||||
return false;
|
||||
|
||||
PaDeviceIndex n = ::Pa_GetDeviceCount();
|
||||
if (n <= 0)
|
||||
return false;
|
||||
|
||||
for (PaDeviceIndex i = 0; i < n; i++) {
|
||||
const PaDeviceInfo* device = ::Pa_GetDeviceInfo(i);
|
||||
|
||||
if (device->hostApi != apiIndex)
|
||||
continue;
|
||||
|
||||
wxString name(device->name, wxConvLocal);
|
||||
|
||||
if (!m_readDevice.IsEmpty() && m_readDevice.IsSameAs(name) && device->maxInputChannels > 0)
|
||||
inDev = i;
|
||||
|
||||
if (!m_writeDevice.IsEmpty() && m_writeDevice.IsSameAs(name) && device->maxOutputChannels > 0)
|
||||
outDev = i;
|
||||
}
|
||||
|
||||
if (inDev == -1 && outDev == -1)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
wxArrayString CSoundCardReaderWriter::m_readDevices;
|
||||
wxArrayString CSoundCardReaderWriter::m_writeDevices;
|
||||
|
||||
CSoundCardReaderWriter::CSoundCardReaderWriter(const wxString& readDevice, const wxString& writeDevice, unsigned int sampleRate, unsigned int blockSize) :
|
||||
m_readDevice(readDevice),
|
||||
m_writeDevice(writeDevice),
|
||||
m_sampleRate(sampleRate),
|
||||
m_blockSize(blockSize),
|
||||
m_callback(NULL),
|
||||
m_id(-1),
|
||||
m_reader(NULL),
|
||||
m_writer(NULL)
|
||||
{
|
||||
wxASSERT(sampleRate > 0U);
|
||||
wxASSERT(blockSize > 0U);
|
||||
}
|
||||
|
||||
CSoundCardReaderWriter::~CSoundCardReaderWriter()
|
||||
{
|
||||
}
|
||||
|
||||
wxArrayString CSoundCardReaderWriter::getReadDevices()
|
||||
{
|
||||
snd_ctl_t *handle = NULL;
|
||||
snd_pcm_t *pcm = NULL;
|
||||
char NameString[256];
|
||||
|
||||
wxArrayString devices(m_readDevices);
|
||||
|
||||
snd_ctl_card_info_t* info;
|
||||
snd_ctl_card_info_alloca(&info);
|
||||
|
||||
snd_pcm_info_t* pcminfo;
|
||||
snd_pcm_info_alloca(&pcminfo);
|
||||
|
||||
snd_pcm_hw_params_t* pars;
|
||||
snd_pcm_hw_params_alloca(&pars);
|
||||
|
||||
unsigned min, max;
|
||||
int err;
|
||||
snd_pcm_stream_t stream = SND_PCM_STREAM_CAPTURE;
|
||||
|
||||
int card = -1;
|
||||
while (::snd_card_next(&card) == 0 && card >= 0) {
|
||||
char hwdev[80];
|
||||
::sprintf(hwdev, "hw:%d", card);
|
||||
|
||||
if (::snd_ctl_open(&handle, hwdev, 0) < 0)
|
||||
continue;
|
||||
|
||||
::snd_ctl_card_info(handle, info);
|
||||
::snd_ctl_card_info_get_name(info);
|
||||
|
||||
int dev = -1;
|
||||
while (::snd_ctl_pcm_next_device(handle, &dev) == 0 && dev >= 0) {
|
||||
::snd_pcm_info_set_device(pcminfo, dev);
|
||||
::snd_pcm_info_set_subdevice(pcminfo, 0);
|
||||
::snd_pcm_info_set_stream(pcminfo, stream);
|
||||
|
||||
err = ::snd_ctl_pcm_info(handle, pcminfo);
|
||||
if (err != -ENOENT) {
|
||||
::sprintf(hwdev, "hw:%d,%d", card, dev);
|
||||
|
||||
if (::snd_pcm_open(&pcm, hwdev, stream, SND_PCM_NONBLOCK) < 0)
|
||||
continue;
|
||||
|
||||
::snd_pcm_hw_params_any(pcm, pars);
|
||||
::snd_pcm_hw_params_get_channels_min(pars, &min);
|
||||
::snd_pcm_hw_params_get_channels_max(pars, &max);
|
||||
|
||||
::snd_pcm_hw_params_get_rate_min(pars, &min, NULL);
|
||||
::snd_pcm_hw_params_get_rate_max(pars, &max, NULL);
|
||||
|
||||
::sprintf(NameString, "hw:%d,%d %s(%s)",
|
||||
card, dev,
|
||||
::snd_pcm_info_get_name(pcminfo),
|
||||
snd_ctl_card_info_get_name(info));
|
||||
|
||||
wxString name(NameString, wxConvLocal);
|
||||
devices.Add(name);
|
||||
|
||||
::snd_pcm_close(pcm);
|
||||
pcm = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
::snd_ctl_close(handle);
|
||||
}
|
||||
|
||||
return devices;
|
||||
}
|
||||
|
||||
wxArrayString CSoundCardReaderWriter::getWriteDevices()
|
||||
{
|
||||
snd_ctl_t *handle = NULL;
|
||||
snd_pcm_t *pcm = NULL;
|
||||
char NameString[256];
|
||||
|
||||
wxArrayString devices(m_writeDevices);
|
||||
|
||||
snd_ctl_card_info_t* info;
|
||||
snd_ctl_card_info_alloca(&info);
|
||||
|
||||
snd_pcm_info_t* pcminfo;
|
||||
snd_pcm_info_alloca(&pcminfo);
|
||||
|
||||
snd_pcm_hw_params_t* pars;
|
||||
snd_pcm_hw_params_alloca(&pars);
|
||||
|
||||
unsigned min, max;
|
||||
int err;
|
||||
snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK;
|
||||
|
||||
int card = -1;
|
||||
while (::snd_card_next(&card) == 0 && card >= 0) {
|
||||
char hwdev[80];
|
||||
::sprintf(hwdev, "hw:%d", card);
|
||||
|
||||
if (::snd_ctl_open(&handle, hwdev, 0) < 0)
|
||||
continue;
|
||||
|
||||
::snd_ctl_card_info(handle, info);
|
||||
::snd_ctl_card_info_get_name(info);
|
||||
|
||||
int dev = -1;
|
||||
while (::snd_ctl_pcm_next_device(handle, &dev) == 0 && dev >= 0) {
|
||||
::snd_pcm_info_set_device(pcminfo, dev);
|
||||
::snd_pcm_info_set_subdevice(pcminfo, 0);
|
||||
::snd_pcm_info_set_stream(pcminfo, stream);
|
||||
|
||||
err= ::snd_ctl_pcm_info(handle, pcminfo);
|
||||
if (err != -ENOENT) {
|
||||
::sprintf(hwdev, "hw:%d,%d", card, dev);
|
||||
|
||||
if (::snd_pcm_open(&pcm, hwdev, stream, SND_PCM_NONBLOCK) < 0)
|
||||
continue;
|
||||
|
||||
::snd_pcm_hw_params_any(pcm, pars);
|
||||
::snd_pcm_hw_params_get_channels_min(pars, &min);
|
||||
::snd_pcm_hw_params_get_channels_max(pars, &max);
|
||||
|
||||
::snd_pcm_hw_params_get_rate_min(pars, &min, NULL);
|
||||
::snd_pcm_hw_params_get_rate_max(pars, &max, NULL);
|
||||
|
||||
::sprintf(NameString, "hw:%d,%d %s(%s)",
|
||||
card, dev,
|
||||
::snd_pcm_info_get_name(pcminfo),
|
||||
::snd_ctl_card_info_get_name(info));
|
||||
|
||||
wxString name(NameString, wxConvLocal);
|
||||
devices.Add(name);
|
||||
|
||||
::snd_pcm_close(pcm);
|
||||
pcm = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
::snd_ctl_close(handle);
|
||||
}
|
||||
|
||||
return devices;
|
||||
}
|
||||
|
||||
void CSoundCardReaderWriter::setCallback(IAudioCallback* callback, int id)
|
||||
{
|
||||
wxASSERT(callback != NULL);
|
||||
|
||||
m_callback = callback;
|
||||
|
||||
m_id = id;
|
||||
}
|
||||
|
||||
bool CSoundCardReaderWriter::open()
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
char buf1[100];
|
||||
char buf2[100];
|
||||
char* ptr;
|
||||
|
||||
// Store the opened devices because ALSA won't enumerate them
|
||||
m_readDevices.Add(m_readDevice);
|
||||
m_writeDevices.Add(m_writeDevice);
|
||||
|
||||
::strcpy(buf1, (const char*)m_writeDevice.mb_str(wxConvUTF8));
|
||||
::strcpy(buf2, (const char*)m_readDevice.mb_str(wxConvUTF8));
|
||||
|
||||
ptr = ::strchr(buf1, ' ');
|
||||
if (ptr) *ptr = 0; // Get Device part of name
|
||||
|
||||
ptr = ::strchr(buf2, ' ');
|
||||
if (ptr) *ptr = 0; // Get Device part of name
|
||||
|
||||
wxString writeDevice(buf1, wxConvLocal);
|
||||
wxString readDevice(buf2, wxConvLocal);
|
||||
|
||||
snd_pcm_t* playHandle = NULL;
|
||||
if ((err = ::snd_pcm_open(&playHandle, buf1, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
|
||||
wxString error(::snd_strerror(err), wxConvLocal);
|
||||
wxLogError(wxT("Cannot open playback audio device %s (%s)"), writeDevice.c_str(), error.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
snd_pcm_hw_params_t* hw_params;
|
||||
if ((err = ::snd_pcm_hw_params_malloc(&hw_params)) < 0) {
|
||||
wxString error(::snd_strerror(err), wxConvLocal);
|
||||
wxLogError(wxT("Cannot allocate hardware parameter structure (%s)"), error.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((err = ::snd_pcm_hw_params_any(playHandle, hw_params)) < 0) {
|
||||
wxString error(::snd_strerror(err), wxConvLocal);
|
||||
wxLogError(wxT("Cannot initialize hardware parameter structure (%s)"), error.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((err = ::snd_pcm_hw_params_set_access(playHandle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
|
||||
wxString error(::snd_strerror(err), wxConvLocal);
|
||||
wxLogError(wxT("Cannot set access type (%s)"), error.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((err = ::snd_pcm_hw_params_set_format(playHandle, hw_params, SND_PCM_FORMAT_S16_LE)) < 0) {
|
||||
wxString error(::snd_strerror(err), wxConvLocal);
|
||||
wxLogError(wxT("Cannot set sample format (%s)"), error.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((err = ::snd_pcm_hw_params_set_rate(playHandle, hw_params, m_sampleRate, 0)) < 0) {
|
||||
wxString error(::snd_strerror(err), wxConvLocal);
|
||||
wxLogError(wxT("Cannot set sample rate (%s)"), error.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned int playChannels = 1U;
|
||||
|
||||
if ((err = ::snd_pcm_hw_params_set_channels(playHandle, hw_params, 1)) < 0) {
|
||||
playChannels = 2U;
|
||||
|
||||
if ((err = ::snd_pcm_hw_params_set_channels(playHandle, hw_params, 2)) < 0) {
|
||||
wxString error(::snd_strerror(err), wxConvLocal);
|
||||
wxLogError(wxT("Cannot play set channel count (%s)"), error.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ((err = ::snd_pcm_hw_params(playHandle, hw_params)) < 0) {
|
||||
wxString error(::snd_strerror(err), wxConvLocal);
|
||||
wxLogError(wxT("Cannot set parameters (%s)"), error.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
::snd_pcm_hw_params_free(hw_params);
|
||||
|
||||
if ((err = ::snd_pcm_prepare(playHandle)) < 0) {
|
||||
wxString error(::snd_strerror(err), wxConvLocal);
|
||||
wxLogError(wxT("Cannot prepare audio interface for use (%s)"), error.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Open Capture
|
||||
snd_pcm_t* recHandle = NULL;
|
||||
if ((err = ::snd_pcm_open(&recHandle, buf2, SND_PCM_STREAM_CAPTURE, 0)) < 0) {
|
||||
wxString error(::snd_strerror(err), wxConvLocal);
|
||||
wxLogError(wxT("Cannot open capture audio device %s (%s)"), readDevice.c_str(), error.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((err = ::snd_pcm_hw_params_malloc(&hw_params)) < 0) {
|
||||
wxString error(::snd_strerror(err), wxConvLocal);
|
||||
wxLogError(wxT("Cannot allocate hardware parameter structure (%s)"), error.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((err = ::snd_pcm_hw_params_any(recHandle, hw_params)) < 0) {
|
||||
wxString error(::snd_strerror(err), wxConvLocal);
|
||||
wxLogError(wxT("Cannot initialize hardware parameter structure (%s)"), error.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((err = ::snd_pcm_hw_params_set_access(recHandle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
|
||||
wxString error(::snd_strerror(err), wxConvLocal);
|
||||
wxLogError(wxT("Cannot set access type (%s)"), error.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((err = ::snd_pcm_hw_params_set_format(recHandle, hw_params, SND_PCM_FORMAT_S16_LE)) < 0) {
|
||||
wxString error(::snd_strerror(err), wxConvLocal);
|
||||
wxLogError(wxT("Cannot set sample format (%s)"), error.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((err = ::snd_pcm_hw_params_set_rate(recHandle, hw_params, m_sampleRate, 0)) < 0) {
|
||||
wxString error(::snd_strerror(err), wxConvLocal);
|
||||
wxLogError(wxT("Cannot set sample rate (%s)"), error.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned int recChannels = 1U;
|
||||
|
||||
if ((err = ::snd_pcm_hw_params_set_channels(recHandle, hw_params, 1)) < 0) {
|
||||
recChannels = 2U;
|
||||
|
||||
if ((err = ::snd_pcm_hw_params_set_channels (recHandle, hw_params, 2)) < 0) {
|
||||
wxString error(::snd_strerror(err), wxConvLocal);
|
||||
wxLogError(wxT("Cannot rec set channel count (%s)"), error.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ((err = ::snd_pcm_hw_params(recHandle, hw_params)) < 0) {
|
||||
wxString error(::snd_strerror(err), wxConvLocal);
|
||||
wxLogError(wxT("Cannot set parameters (%s)"), error.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
::snd_pcm_hw_params_free(hw_params);
|
||||
|
||||
if ((err = ::snd_pcm_prepare(recHandle)) < 0) {
|
||||
wxString error(::snd_strerror(err), wxConvLocal);
|
||||
wxLogError(wxT("Cannot prepare audio interface for use (%s)"), error.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
short samples[256];
|
||||
for (unsigned int i = 0U; i < 10U; ++i)
|
||||
::snd_pcm_readi(recHandle, samples, 128);
|
||||
|
||||
wxLogMessage(wxT("Opened %s %s Rate %u"), writeDevice.c_str(), readDevice.c_str(), m_sampleRate);
|
||||
|
||||
m_reader = new CSoundCardReader(recHandle, m_blockSize, recChannels, m_callback, m_id);
|
||||
m_writer = new CSoundCardWriter(playHandle, m_blockSize, playChannels, m_callback, m_id);
|
||||
|
||||
m_reader->Create();
|
||||
m_reader->Run();
|
||||
|
||||
m_writer->Create();
|
||||
m_writer->Run();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CSoundCardReaderWriter::close()
|
||||
{
|
||||
m_reader->kill();
|
||||
m_writer->kill();
|
||||
|
||||
m_reader->Wait();
|
||||
m_writer->Wait();
|
||||
}
|
||||
|
||||
bool CSoundCardReaderWriter::isWriterBusy() const
|
||||
{
|
||||
return m_writer->isBusy();
|
||||
}
|
||||
|
||||
CSoundCardReader::CSoundCardReader(snd_pcm_t* handle, unsigned int blockSize, unsigned int channels, IAudioCallback* callback, int id) :
|
||||
wxThread(wxTHREAD_JOINABLE),
|
||||
m_handle(handle),
|
||||
m_blockSize(blockSize),
|
||||
m_channels(channels),
|
||||
m_callback(callback),
|
||||
m_id(id),
|
||||
m_killed(false),
|
||||
m_buffer(NULL),
|
||||
m_samples(NULL)
|
||||
{
|
||||
wxASSERT(handle != NULL);
|
||||
wxASSERT(blockSize > 0U);
|
||||
wxASSERT(channels == 1U || channels == 2U);
|
||||
wxASSERT(callback != NULL);
|
||||
|
||||
m_buffer = new wxFloat32[blockSize];
|
||||
m_samples = new short[2U * blockSize];
|
||||
}
|
||||
|
||||
CSoundCardReader::~CSoundCardReader()
|
||||
{
|
||||
delete[] m_buffer;
|
||||
delete[] m_samples;
|
||||
}
|
||||
|
||||
void* CSoundCardReader::Entry()
|
||||
{
|
||||
wxLogMessage(wxT("Starting ALSA reader thread"));
|
||||
|
||||
while (!m_killed) {
|
||||
snd_pcm_sframes_t ret;
|
||||
while ((ret = ::snd_pcm_readi(m_handle, m_samples, m_blockSize)) < 0) {
|
||||
if (ret != -EPIPE) {
|
||||
wxString error(::snd_strerror(ret), wxConvLocal);
|
||||
wxLogWarning(wxT("snd_pcm_readi returned %d (%s)"), ret, error.c_str());
|
||||
}
|
||||
|
||||
::snd_pcm_recover(m_handle, ret, 1);
|
||||
}
|
||||
|
||||
if (m_channels == 1U) {
|
||||
for (int n = 0; n < ret; n++)
|
||||
m_buffer[n] = wxFloat32(m_samples[n]) / 32768.0F;
|
||||
} else {
|
||||
int i = 0;
|
||||
for (int n = 0; n < (ret * 2); n += 2)
|
||||
m_buffer[i++] = wxFloat32(m_samples[n + 1]) / 32768.0F;
|
||||
}
|
||||
|
||||
m_callback->readCallback(m_buffer, (unsigned int)ret, m_id);
|
||||
}
|
||||
|
||||
wxLogMessage(wxT("Stopping ALSA reader thread"));
|
||||
|
||||
::snd_pcm_close(m_handle);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void CSoundCardReader::kill()
|
||||
{
|
||||
m_killed = true;
|
||||
}
|
||||
|
||||
CSoundCardWriter::CSoundCardWriter(snd_pcm_t* handle, unsigned int blockSize, unsigned int channels, IAudioCallback* callback, int id) :
|
||||
wxThread(wxTHREAD_JOINABLE),
|
||||
m_handle(handle),
|
||||
m_blockSize(blockSize),
|
||||
m_channels(channels),
|
||||
m_callback(callback),
|
||||
m_id(id),
|
||||
m_killed(false),
|
||||
m_buffer(NULL),
|
||||
m_samples(NULL)
|
||||
{
|
||||
wxASSERT(handle != NULL);
|
||||
wxASSERT(blockSize > 0U);
|
||||
wxASSERT(channels == 1U || channels == 2U);
|
||||
wxASSERT(callback != NULL);
|
||||
|
||||
m_buffer = new wxFloat32[2U * blockSize];
|
||||
m_samples = new short[4U * blockSize];
|
||||
}
|
||||
|
||||
CSoundCardWriter::~CSoundCardWriter()
|
||||
{
|
||||
delete[] m_buffer;
|
||||
delete[] m_samples;
|
||||
}
|
||||
|
||||
void* CSoundCardWriter::Entry()
|
||||
{
|
||||
wxLogMessage(wxT("Starting ALSA writer thread"));
|
||||
|
||||
while (!m_killed) {
|
||||
unsigned int nSamples = 2U * m_blockSize;
|
||||
m_callback->writeCallback(m_buffer, nSamples, m_id);
|
||||
|
||||
if (nSamples == 0U) {
|
||||
Sleep(5UL);
|
||||
} else {
|
||||
if (m_channels == 1U) {
|
||||
for (unsigned int n = 0U; n < nSamples; n++)
|
||||
m_samples[n] = short(m_buffer[n] * 32767.0F);
|
||||
} else {
|
||||
unsigned int i = 0U;
|
||||
for (unsigned int n = 0U; n < nSamples; n++) {
|
||||
short sample = short(m_buffer[n] * 32767.0F);
|
||||
m_samples[i++] = sample;
|
||||
m_samples[i++] = sample; // Same value to both channels
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int offset = 0U;
|
||||
snd_pcm_sframes_t ret;
|
||||
while ((ret = ::snd_pcm_writei(m_handle, m_samples + offset, nSamples - offset)) != (nSamples - offset)) {
|
||||
if (ret < 0) {
|
||||
if (ret != -EPIPE) {
|
||||
wxString error(::snd_strerror(ret), wxConvLocal);
|
||||
wxLogWarning(wxT("snd_pcm_writei returned %d (%s)"), ret, error.c_str());
|
||||
}
|
||||
|
||||
::snd_pcm_recover(m_handle, ret, 1);
|
||||
} else {
|
||||
offset += (unsigned int)ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wxLogMessage(wxT("Stopping ALSA writer thread"));
|
||||
|
||||
::snd_pcm_close(m_handle);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void CSoundCardWriter::kill()
|
||||
{
|
||||
m_killed = true;
|
||||
}
|
||||
|
||||
bool CSoundCardWriter::isBusy() const
|
||||
{
|
||||
snd_pcm_state_t state = ::snd_pcm_state(m_handle);
|
||||
|
||||
return state == SND_PCM_STATE_RUNNING || state == SND_PCM_STATE_DRAINING;
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (C) 2009,2010,2015 by Jonathan Naylor, G4KLX
|
||||
* Copyright (C) 2014 by John Wiseman, G8BPQ
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef SoundCardReaderWriter_H
|
||||
#define SoundCardReaderWriter_H
|
||||
|
||||
#include "AudioCallback.h"
|
||||
|
||||
#include <wx/wx.h>
|
||||
|
||||
#if (defined(__APPLE__) && defined(__MACH__)) || defined(__WINDOWS__)
|
||||
|
||||
#include "portaudio.h"
|
||||
|
||||
class CSoundCardReaderWriter {
|
||||
public:
|
||||
CSoundCardReaderWriter(const wxString& readDevice, const wxString& writeDevice, unsigned int sampleRate, unsigned int blockSize);
|
||||
~CSoundCardReaderWriter();
|
||||
|
||||
void setCallback(IAudioCallback* callback, int id);
|
||||
bool open();
|
||||
void close();
|
||||
|
||||
void callback(const wxFloat32* input, wxFloat32* output, unsigned int nSamples);
|
||||
|
||||
static wxArrayString getReadDevices();
|
||||
static wxArrayString getWriteDevices();
|
||||
|
||||
private:
|
||||
wxString m_readDevice;
|
||||
wxString m_writeDevice;
|
||||
unsigned int m_sampleRate;
|
||||
unsigned int m_blockSize;
|
||||
IAudioCallback* m_callback;
|
||||
int m_id;
|
||||
PaStream* m_stream;
|
||||
|
||||
bool convertNameToDevices(PaDeviceIndex& inDev, PaDeviceIndex& outDev);
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
#include <alsa/asoundlib.h>
|
||||
|
||||
class CSoundCardReader : public wxThread {
|
||||
public:
|
||||
CSoundCardReader(snd_pcm_t* handle, unsigned int blockSize, unsigned int channels, IAudioCallback* callback, int id);
|
||||
virtual ~CSoundCardReader();
|
||||
|
||||
virtual void* Entry();
|
||||
|
||||
virtual void kill();
|
||||
|
||||
private:
|
||||
snd_pcm_t* m_handle;
|
||||
unsigned int m_blockSize;
|
||||
unsigned int m_channels;
|
||||
IAudioCallback* m_callback;
|
||||
int m_id;
|
||||
bool m_killed;
|
||||
wxFloat32* m_buffer;
|
||||
short* m_samples;
|
||||
};
|
||||
|
||||
class CSoundCardWriter : public wxThread {
|
||||
public:
|
||||
CSoundCardWriter(snd_pcm_t* handle, unsigned int blockSize, unsigned int channels, IAudioCallback* callback, int id);
|
||||
virtual ~CSoundCardWriter();
|
||||
|
||||
virtual void* Entry();
|
||||
|
||||
virtual void kill();
|
||||
|
||||
virtual bool isBusy() const;
|
||||
|
||||
private:
|
||||
snd_pcm_t* m_handle;
|
||||
unsigned int m_blockSize;
|
||||
unsigned int m_channels;
|
||||
IAudioCallback* m_callback;
|
||||
int m_id;
|
||||
bool m_killed;
|
||||
wxFloat32* m_buffer;
|
||||
short* m_samples;
|
||||
};
|
||||
|
||||
class CSoundCardReaderWriter {
|
||||
public:
|
||||
CSoundCardReaderWriter(const wxString& readDevice, const wxString& writeDevice, unsigned int sampleRate, unsigned int blockSize);
|
||||
~CSoundCardReaderWriter();
|
||||
|
||||
void setCallback(IAudioCallback* callback, int id);
|
||||
bool open();
|
||||
void close();
|
||||
|
||||
bool isWriterBusy() const;
|
||||
|
||||
static wxArrayString getReadDevices();
|
||||
static wxArrayString getWriteDevices();
|
||||
|
||||
private:
|
||||
wxString m_readDevice;
|
||||
wxString m_writeDevice;
|
||||
unsigned int m_sampleRate;
|
||||
unsigned int m_blockSize;
|
||||
IAudioCallback* m_callback;
|
||||
int m_id;
|
||||
CSoundCardReader* m_reader;
|
||||
CSoundCardWriter* m_writer;
|
||||
|
||||
static wxArrayString m_readDevices;
|
||||
static wxArrayString m_writeDevices;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue