From b6f7e4a7a15dbc4cba3630c51d9d9ad2899f65ef Mon Sep 17 00:00:00 2001 From: StevenHAtRFDesign Date: Sun, 25 Feb 2024 13:58:32 +1000 Subject: [PATCH] Sik radio update 2024-1-18 (#3277) * RFD900 tools 2.12 - Fixed tooltip text for GPI1_1R/CIN and GPI1_1R/COUT * RFD900 tools 2.13 - Now if a parameter fails to load, that same parameter is not saved back to the modem. * RFD900 Tools 2.14 - Resolved issue in which modem firmware could not be reprogrammed immediately after programming. * RFD900x restrictions for firmware programming for modems locked to country now appears to work well. However, programming RFD900+ seems to have regressed. * Resolved issues with RFD900+ modem in which it couldn't load settings immediately after programming firmware. * Now parses freq/band and antenna options from RFD900x * 2.15 - Added firmware programming certified check for RFD900X/UX. Added SBUS, freq/band and antenna parameters * for RFD900x. * 2.16 - Resolved issue in which it was looking for the country string in the wrong part of the ATI command response. * 2.17 - Resolved issue in which pin wasn't being set to output/input when RC out / SBUS out checked / unchecked. * RFD900 Tools 2.18 - Added text boxes to settings view for local and remote modems to display country code if locked to a country, * as requested by Moreton. * 2.19 - Fixed bug in which programming RFD900x firmware whose file size was a multiple of 128 bytes failed. * 2.20 - Made changes so that it could update firmware and load settings from a modem with multipoint firmware. * 2.21 - Now for modems which use the xmodem protocol for firmware updates (RFD900x and RFD900ux), it waits for * and acknowledgement of the EOT packet before sending the BOOTNEW command. This may or may not * resolve the intermittent issue in which firmware update results in the modem staying in bootloader mode. * Now programs version 3.01 as the reference firmware for RFD900x to check certified (instead of 3.00). * 2.21 - Now for modems which use the xmodem protocol for firmware updates (RFD900x and RFD900ux), it waits for * and acknowledgement of the EOT packet before sending the BOOTNEW command. This may or may not * resolve the intermittent issue in which firmware update results in the modem staying in bootloader mode. * Now programs version 3.01 as the reference firmware for RFD900x to check certified (instead of 3.00). * RFD900 Tools 2.22 - Changed "Set PPM Fail safe" feature so there is one button per local/remote modem. Made it so it is enabled * depending on GPO1_1R/COUT being set. * Now has better handling of the case in which it can't put the modem into RSSI reprting mode. * 2.24 - Improved instructions to user in message boxes. Improved handling of cases where it was unable to communicate * RFD900 Tools 2.25 - Now does auto disconnect/reconnect of serial port after every firmware programming regardless of success or failure. * 2.26 - Now sets status text to "Programming Failed. (Try Again?)" if it fails to determine which mode the modem is in when trying to program firmware. * 2.28 - Now has a manufacturing page/tab for locking down modems to a country. * 2.29 - Resolved bug in which the value ranges available from the remote modem weren't being parsed and used in the combo box e.g. for the min and max frequency ranges. Also, added a work-around for an issue in the RFDSiK RFD900x 3.07 firmware in which it didn't return the complete set of parameters in response to an RTI5? command. * 2.30 - For RFD900x multipoint firmware, now allows correct broadcast id for nodedestination parameter. * RFD900 tools 2.32 - Supports programming of RFD900ux modems. * 2.33 - Corrected available frequency ranges for RFD900+ * 2.34 - Now handles new settings GPO1_1SBUSIN, GPO1_1SBUSOUT, GPO1_3STATLED, and GPO1_0TXEN485 * 2.35 - Fixed bug in which re-loading settings when AES was previously enabled would cause it to disable AES. * 2.36 - Added a retry option message window for when programming firmware fails. * 2.37 - Resolved issue in which a MAX_WINDOW setting value which wasn't a multiple of 20 failed to be set in the settings GUI. * 2.38 - Manufacturing tab now includes lockdown for EU and India. * 2.39 - For checkbox settings for which the modem specifies (in the ATI5? query response) only one option, it now greys-out the checkbox. This was to cater to the X modem 3.15 firmware in which the ECC paramater can't be changed and is always false. * 2.40 - Now for remote modems which do not specify valid settings ranges, if the remote modem has the same firmware version as the * 2.42 - Now gets valid parameter ranges out of remote modem for newer firmware which doesn't support the RTI5? query. Now supports the * 2.43 - Resolved freezing-up issue which happened for older or non-SiK modem firmware which doesn't yet have ATI10/RTI10 command. Tidied up GUI layout. * 2.44 - Resolved mavlink setting problem. * 2.45 - There is now a 50ms delay between '+' characters transmitted to modem when doing the +++ sequence to change mode from transparent to AT command mode, * 2.46 - A command line switch is now available to force programming of firmware into modems regardless of type of modem. Removed 50ms delay between '+' characters transmitted to modem when doing the +++ sequence to change mode from transparent to AT command mode, as this is not required by any firmware. * 2.47 - Added GPIO1_1FUNC settings for RFD900x async firmware 2.69. * 2.48 - RSSI page now writes log to the user's documents folder, instead of the program folder, to prevent exception. * 2.49 - Hardened up code to reduce crashes when switching between terminal, RSSI and settings pages. * 2.50 - Added support for FSFRAMELOSS, GPI1_2AUXIN and GPO1_3AUXOUT parameters. * RFDTools 2.51 - Fixed error message problem when used with multipoint firmware 2.90. * RFDTools 2.52 - Now uses a more reliable means of reading country code from modems than looking at the string returned by ATI command. * 2.53 - For multipoint firmware, made 255 available as an option for destination node ID. * 2.54 - Changed encryption key text box max length from 32 to 64 hex numerals (for 256 bit encryption). * 2.55 - Changed encryption key text box max length from 32 to 64 hex numerals (for 256 bit encryption). * 2.56 - Now recognises country code 255 as not locked to country. * RFDTools 2.57 - Now before programming firmware, checks whether modem says it is DINIO. If it says it is DINIO, only allows DINIO firmware to be programmed into modem. * 2.58 - Now supports RFD X rev 2. Now checks that band is compatible with country before locking to a country. * RFDTools 2.60 - Now supports RFD X rev 2 programming firmware when baud rate other than 57600bps is used. * 2.61 - Now supports RFD UX rev 2. * 2.63 - Settings GUI can now dynamically shows settings read from the modem which are unknown to this software. Supports the RSSI_IN_DBM, AUXSER_SPEED and AIR_FRAMELEN settings. * RFDTools 2.64 - Can now save settings to file and load settings from file. * 2.65 - Now checks whether min freq is greater than max freq before saving settings to modem(s). Resolved issue in which itshowed erroneous "Set command error" message when saving encryption key to modem. * RFD Tools 2.66 - Fixed bug for multipoint firmware in which it showed an error message of 'Encryption key not valid hex number <=32[/64] hex numerals' when saving change to encryption level setting to the modem. * Fix firmware download bug where if the firmware file were read only (or in a read only directory) trying to download it would result in an error because the file could not be opened. Resolved by making the file stream open the file in read only mode. --------- Co-authored-by: Martin Keeling --- MissionPlanner.csproj | 5 + Radio/ComPort.cs | 31 + Radio/Models.cs | 62 +- Radio/Sikradio.Designer.cs | 1072 +++-- Radio/Sikradio.cs | 2040 ++++++--- Radio/Sikradio.resx | 4098 +++++++++++------- Radio/Sikradio.zh-Hant.resx | 1128 ++++- Radio/Sikradio.zh-TW.resx | 1128 ++++- Radio/Uploader.cs | 3 + Radio/XModem.cs | 142 +- SikRadio/ComPort.cs | 18 + SikRadio/Config.cs | 58 +- SikRadio/Config.resx | 6 +- SikRadio/ISikRadioForm.cs | 1 + SikRadio/Program.cs | 34 +- SikRadio/Properties/AssemblyInfo.cs | 2 +- SikRadio/Properties/Resources.Designer.cs | 10 + SikRadio/Properties/Resources.resx | 3 + SikRadio/RFD900.cs | 2405 ++++++++-- SikRadio/RFDLib/Array.cs | 24 + SikRadio/RFDLib/Collections.cs | 36 + SikRadio/RFDLib/GUI/Settings.cs | 262 ++ SikRadio/RFDLib/IO/ATCommand.cs | 209 + SikRadio/RFDLib/IO/SerialPort/SerialPort.cs | 137 + SikRadio/RFDLib/RFDLib.cs | 49 + SikRadio/RFDLib/Text.cs | 18 +- SikRadio/Resources/RFDSiK V3.00 rfd900x.bin | Bin 0 -> 110288 bytes SikRadio/Resources/RFDSiK V3.01 rfd900x.bin | Bin 0 -> 100864 bytes SikRadio/Resources/RFDSiK V3.08 rfd900ux.bin | Bin 0 -> 103708 bytes SikRadio/Rssi.Designer.cs | 35 +- SikRadio/Rssi.cs | 150 +- SikRadio/SikRadio.csproj | 9 + SikRadio/Terminal.cs | 36 +- 33 files changed, 10040 insertions(+), 3171 deletions(-) create mode 100644 Radio/ComPort.cs create mode 100644 SikRadio/ComPort.cs create mode 100644 SikRadio/RFDLib/Collections.cs create mode 100644 SikRadio/RFDLib/GUI/Settings.cs create mode 100644 SikRadio/RFDLib/IO/ATCommand.cs create mode 100644 SikRadio/RFDLib/IO/SerialPort/SerialPort.cs create mode 100644 SikRadio/RFDLib/RFDLib.cs create mode 100644 SikRadio/Resources/RFDSiK V3.00 rfd900x.bin create mode 100644 SikRadio/Resources/RFDSiK V3.01 rfd900x.bin create mode 100644 SikRadio/Resources/RFDSiK V3.08 rfd900ux.bin diff --git a/MissionPlanner.csproj b/MissionPlanner.csproj index a33c1dcda6..a26bfdbb6f 100644 --- a/MissionPlanner.csproj +++ b/MissionPlanner.csproj @@ -1022,7 +1022,11 @@ + + + + Form Splash.cs @@ -1576,6 +1580,7 @@ Form + UserControl diff --git a/Radio/ComPort.cs b/Radio/ComPort.cs new file mode 100644 index 0000000000..043272b30a --- /dev/null +++ b/Radio/ComPort.cs @@ -0,0 +1,31 @@ +using System; +using MissionPlanner.Comms; + +namespace MissionPlanner.Radio +{ + public static class ComPort + { + static ICommsSerial _Port; + + public static ICommsSerial GetComPortForSiKRadio() + { + if (_Port == null) + { + MainV2.comPort.Close(); + MissionPlanner.Radio.Sikradio.Connect(ref _Port); + } + + return _Port; + } + + public static void FinishedWithComPortForSiKRadio() + { + if (_Port != null) + { + _Port.Dispose(); + _Port = null; + } + } + } + +} \ No newline at end of file diff --git a/Radio/Models.cs b/Radio/Models.cs index 9832f02390..eae4eda095 100644 --- a/Radio/Models.cs +++ b/Radio/Models.cs @@ -37,6 +37,7 @@ class ExtraParamControlsSet LabelComboBoxPair TXENCAP; LabelComboBoxPair RXENCAP; LabelComboBoxPair MAX_DATA; + LabelComboBoxPair MAX_RETRIES; IEnumerable Others; bool Remote; @@ -45,6 +46,7 @@ public ExtraParamControlsSet(Label lblNodeID, ComboBox cmbNodeID, Label lblTXENCAP, ComboBox cmbTXENCAP, Label lblRXENCAP, ComboBox cmbRXENCAP, Label lblMAX_DATA, ComboBox cmbMAX_DATA, + Label lblMAX_RETRIES, ComboBox cmbMAX_RETRIES, Control[] Others, bool Remote) { NodeID = new LabelComboBoxPair(lblNodeID, cmbNodeID); @@ -52,12 +54,25 @@ public ExtraParamControlsSet(Label lblNodeID, ComboBox cmbNodeID, TXENCAP = new LabelComboBoxPair(lblTXENCAP, cmbTXENCAP); RXENCAP = new LabelComboBoxPair(lblRXENCAP, cmbRXENCAP); MAX_DATA = new LabelComboBoxPair(lblMAX_DATA, cmbMAX_DATA); + MAX_RETRIES = new LabelComboBoxPair(lblMAX_RETRIES, cmbMAX_RETRIES); this.Others = Others; this.Remote = Remote; } - public void SetModel(Model M) + /// + /// Reconfigure the GUI for the given modem firmware type + /// + /// The modem firmware type. + /// The settings retrieved from the modem. Must not be null. + public void SetModel(Model M, Dictionary Settings) { + NodeID.Reset(); + DestID.Reset(); + TXENCAP.Reset(); + RXENCAP.Reset(); + MAX_DATA.Reset(); + MAX_RETRIES.Reset(); + string Prefix = Remote ? "R" : ""; switch (M) @@ -73,7 +88,7 @@ public void SetModel(Model M) RXENCAP.ComboBox.Name = Prefix + "NODECOUNT"; RXENCAP.Label.Text = "Node Count"; NodeID.ComboBox.DataSource = Sikradio.Range(0, 1, 29); - var Temp = (List)Sikradio.Range(0, 1, 29); + var Temp = new List(Sikradio.Range(0, 1, 29)); Temp.Add(65535); DestID.ComboBox.DataSource = Temp; TXENCAP.ComboBox.DataSource = Sikradio.Range(0, 1, 1); @@ -103,14 +118,27 @@ public void SetModel(Model M) case Model.MULTIPOINT_X: { DestID.ComboBox.Name = Prefix + "NODEDESTINATION"; - TXENCAP.ComboBox.Name = Prefix + "NODECOUNT"; - TXENCAP.Label.Text = "Node Count"; + if (Settings.ContainsKey("NETCOUNT")) + { + TXENCAP.ComboBox.Name = Prefix + "NETCOUNT"; + TXENCAP.Label.Text = "Net Count"; + } + else + { + TXENCAP.ComboBox.Name = Prefix + "NODECOUNT"; + TXENCAP.Label.Text = "Node Count"; + } RXENCAP.ComboBox.Name = Prefix + "SERBREAKMS10"; RXENCAP.Label.Text = "Ser. brk. x10ms"; - MAX_DATA.ComboBox.Name = "GPO1_3STATLED"; - MAX_DATA.Label.Text = "GPO1_\n3STATLED"; + if (Settings.ContainsKey("MASTERBACKUP")) + { + MAX_DATA.ComboBox.Name = Prefix + "MASTERBACKUP"; + MAX_DATA.Label.Text = "Master Bckp"; + } + MAX_RETRIES.ComboBox.Name = Prefix + "RXFRAME"; + MAX_RETRIES.Label.Text = "Rx Frame"; NodeID.ComboBox.DataSource = Sikradio.Range(0, 1, 29); - var Temp = (List)Sikradio.Range(0, 1, 29); + var Temp = new List(Sikradio.Range(0, 1, 29)); Temp.Add(65535); DestID.ComboBox.DataSource = Temp; TXENCAP.ComboBox.DataSource = Sikradio.Range(0, 1, 1); @@ -121,7 +149,14 @@ public void SetModel(Model M) DestID.Visible = true; TXENCAP.Visible = true; RXENCAP.Visible = true; - MAX_DATA.Visible = true; + if (Settings.ContainsKey("MASTERBACKUP")) + { + MAX_DATA.Visible = true; + } + if (Settings.ContainsKey("RXFRAME")) + { + MAX_RETRIES.Visible = true; + } } break; } @@ -134,6 +169,7 @@ void SetAllVisible(bool V) TXENCAP.Visible = V; RXENCAP.Visible = V; MAX_DATA.Visible = V; + MAX_RETRIES.Visible = V; foreach (var x in Others) { x.Visible = V; @@ -145,11 +181,15 @@ class LabelComboBoxPair { public readonly Label Label; public readonly ComboBox ComboBox; + public readonly string OrigName; + public readonly string OrigLabel; public LabelComboBoxPair(Label L, ComboBox C) { this.Label = L; this.ComboBox = C; + OrigName = C.Name; + OrigLabel = L.Text; } public bool Visible @@ -164,5 +204,11 @@ public bool Visible ComboBox.Visible = value; } } + + public void Reset() + { + Label.Text = OrigLabel; + ComboBox.Name = OrigName; + } } } \ No newline at end of file diff --git a/Radio/Sikradio.Designer.cs b/Radio/Sikradio.Designer.cs index cf06b1f7f4..c0a7ae9118 100644 --- a/Radio/Sikradio.Designer.cs +++ b/Radio/Sikradio.Designer.cs @@ -66,6 +66,16 @@ private void InitializeComponent() this.TXPOWER = new System.Windows.Forms.ComboBox(); this.ECC = new System.Windows.Forms.CheckBox(); this.OPPRESEND = new System.Windows.Forms.CheckBox(); + this.ENCRYPTION_LEVEL = new System.Windows.Forms.ComboBox(); + this.RENCRYPTION_LEVEL = new System.Windows.Forms.ComboBox(); + this.FSFRAMELOSS = new System.Windows.Forms.ComboBox(); + this.RFSFRAMELOSS = new System.Windows.Forms.ComboBox(); + this.GPO1_3SBUSIN = new System.Windows.Forms.CheckBox(); + this.RGPO1_3SBUSIN = new System.Windows.Forms.CheckBox(); + this.GPO1_3STATLED = new System.Windows.Forms.CheckBox(); + this.GPO1_0TXEN485 = new System.Windows.Forms.CheckBox(); + this.RGPO1_0TXEN485 = new System.Windows.Forms.CheckBox(); + this.RGPO1_3STATLED = new System.Windows.Forms.CheckBox(); this.RTI = new System.Windows.Forms.TextBox(); this.ATI = new System.Windows.Forms.TextBox(); this.label11 = new System.Windows.Forms.Label(); @@ -73,6 +83,22 @@ private void InitializeComponent() this.lbl_status = new System.Windows.Forms.Label(); this.ATI3 = new System.Windows.Forms.TextBox(); this.groupBoxLocal = new System.Windows.Forms.GroupBox(); + this.label54 = new System.Windows.Forms.Label(); + this.GPO1_3AUXOUT = new System.Windows.Forms.CheckBox(); + this.lblGPO1_3AUXOUT = new System.Windows.Forms.Label(); + this.GPI1_2AUXIN = new System.Windows.Forms.CheckBox(); + this.lblGPI1_2AUXIN = new System.Windows.Forms.Label(); + this.lblGPIO1_1FUNC = new System.Windows.Forms.Label(); + this.GPIO1_1FUNC = new System.Windows.Forms.ComboBox(); + this.lblGPO1_0TXEN485 = new System.Windows.Forms.Label(); + this.lblGPO1_3STATLED = new System.Windows.Forms.Label(); + this.GPO1_3SBUSOUT = new System.Windows.Forms.ComboBox(); + this.label49 = new System.Windows.Forms.Label(); + this.txtCountry = new System.Windows.Forms.TextBox(); + this.label45 = new System.Windows.Forms.Label(); + this.RATE_FREQBAND = new System.Windows.Forms.ComboBox(); + this.lblSBUSOUT = new System.Windows.Forms.Label(); + this.lblSBUSIN = new System.Windows.Forms.Label(); this.btnRandom = new System.Windows.Forms.Button(); this.lblRX_ENCAP_METHOD = new System.Windows.Forms.Label(); this.RX_ENCAP_METHOD = new System.Windows.Forms.ComboBox(); @@ -82,17 +108,17 @@ private void InitializeComponent() this.lblNODEID = new System.Windows.Forms.Label(); this.DESTID = new System.Windows.Forms.ComboBox(); this.NODEID = new System.Windows.Forms.ComboBox(); - this.label40 = new System.Windows.Forms.Label(); - this.label39 = new System.Windows.Forms.Label(); + this.lblGPO1_1R_COUT = new System.Windows.Forms.Label(); + this.lblGPI1_1R_CIN = new System.Windows.Forms.Label(); this.label2 = new System.Windows.Forms.Label(); this.label1 = new System.Windows.Forms.Label(); this.FORMAT = new System.Windows.Forms.TextBox(); this.label3 = new System.Windows.Forms.Label(); - this.label4 = new System.Windows.Forms.Label(); - this.label5 = new System.Windows.Forms.Label(); - this.label6 = new System.Windows.Forms.Label(); - this.label8 = new System.Windows.Forms.Label(); - this.label7 = new System.Windows.Forms.Label(); + this.lblNETID = new System.Windows.Forms.Label(); + this.lblTXPOWER = new System.Windows.Forms.Label(); + this.lblECC = new System.Windows.Forms.Label(); + this.lblOPPRESEND = new System.Windows.Forms.Label(); + this.lblMAVLINK = new System.Windows.Forms.Label(); this.lblANT_MODE = new System.Windows.Forms.Label(); this.ANT_MODE = new System.Windows.Forms.ComboBox(); this.lblSER_BRK_DETMS = new System.Windows.Forms.Label(); @@ -103,21 +129,36 @@ private void InitializeComponent() this.MAX_RETRIES = new System.Windows.Forms.ComboBox(); this.MAX_DATA = new System.Windows.Forms.ComboBox(); this.lblMAX_DATA = new System.Windows.Forms.Label(); - this.ENCRYPTION_LEVEL = new System.Windows.Forms.CheckBox(); - this.label36 = new System.Windows.Forms.Label(); + this.lblENCRYPTION_LEVEL = new System.Windows.Forms.Label(); this.label35 = new System.Windows.Forms.Label(); - this.txt_aeskey = new System.Windows.Forms.TextBox(); - this.label19 = new System.Windows.Forms.Label(); + this.AESKEY = new System.Windows.Forms.TextBox(); + this.lblRTSCTS = new System.Windows.Forms.Label(); this.linkLabel_mavlink = new System.Windows.Forms.LinkLabel(); this.linkLabel_lowlatency = new System.Windows.Forms.LinkLabel(); - this.label18 = new System.Windows.Forms.Label(); - this.label13 = new System.Windows.Forms.Label(); - this.label17 = new System.Windows.Forms.Label(); - this.label16 = new System.Windows.Forms.Label(); - this.label15 = new System.Windows.Forms.Label(); - this.label14 = new System.Windows.Forms.Label(); + this.lblMAX_WINDOW = new System.Windows.Forms.Label(); + this.lblMIN_FREQ = new System.Windows.Forms.Label(); + this.lblLBT_RSSI = new System.Windows.Forms.Label(); + this.lblDUTY_CYCLE = new System.Windows.Forms.Label(); + this.lblNUM_CHANNELS = new System.Windows.Forms.Label(); + this.lblMAX_FREQ = new System.Windows.Forms.Label(); this.ATI2 = new System.Windows.Forms.TextBox(); this.groupBoxRemote = new System.Windows.Forms.GroupBox(); + this.lblRFSFRAMELOSS = new System.Windows.Forms.Label(); + this.RGPO1_3AUXOUT = new System.Windows.Forms.CheckBox(); + this.lblRGPO1_3AUXOUT = new System.Windows.Forms.Label(); + this.RGPI1_2AUXIN = new System.Windows.Forms.CheckBox(); + this.lblRGPI1_2AUXIN = new System.Windows.Forms.Label(); + this.lblRGPIO1_1FUNC = new System.Windows.Forms.Label(); + this.RGPIO1_1FUNC = new System.Windows.Forms.ComboBox(); + this.lblRGPO1_0TXEN485 = new System.Windows.Forms.Label(); + this.lblRGPO1_3STATLED = new System.Windows.Forms.Label(); + this.RGPO1_3SBUSOUT = new System.Windows.Forms.ComboBox(); + this.label50 = new System.Windows.Forms.Label(); + this.txtRCountry = new System.Windows.Forms.TextBox(); + this.lblRSBUSOUT = new System.Windows.Forms.Label(); + this.lblRSBUSIN = new System.Windows.Forms.Label(); + this.label46 = new System.Windows.Forms.Label(); + this.RRATE_FREQBAND = new System.Windows.Forms.ComboBox(); this.lblRRX_ENCAP_METHOD = new System.Windows.Forms.Label(); this.RRX_ENCAP_METHOD = new System.Windows.Forms.ComboBox(); this.lblRTX_ENCAP_METHOD = new System.Windows.Forms.Label(); @@ -126,14 +167,14 @@ private void InitializeComponent() this.lblRNODEID = new System.Windows.Forms.Label(); this.RDESTID = new System.Windows.Forms.ComboBox(); this.RNODEID = new System.Windows.Forms.ComboBox(); - this.label41 = new System.Windows.Forms.Label(); + this.lblRGPO1_1R_COUT = new System.Windows.Forms.Label(); this.RFORMAT = new System.Windows.Forms.TextBox(); - this.label42 = new System.Windows.Forms.Label(); - this.label25 = new System.Windows.Forms.Label(); - this.label26 = new System.Windows.Forms.Label(); - this.label27 = new System.Windows.Forms.Label(); - this.label28 = new System.Windows.Forms.Label(); - this.label29 = new System.Windows.Forms.Label(); + this.lblRGPI1_1R_CIN = new System.Windows.Forms.Label(); + this.lblROPPRESEND = new System.Windows.Forms.Label(); + this.lblRMAVLINK = new System.Windows.Forms.Label(); + this.lblRECC = new System.Windows.Forms.Label(); + this.lblRTXPOWER = new System.Windows.Forms.Label(); + this.lblRNETID = new System.Windows.Forms.Label(); this.label32 = new System.Windows.Forms.Label(); this.label30 = new System.Windows.Forms.Label(); this.label31 = new System.Windows.Forms.Label(); @@ -148,26 +189,32 @@ private void InitializeComponent() this.RMAX_DATA = new System.Windows.Forms.ComboBox(); this.lblRMAX_DATA = new System.Windows.Forms.Label(); this.label38 = new System.Windows.Forms.Label(); - this.RENCRYPTION_LEVEL = new System.Windows.Forms.CheckBox(); - this.txt_Raeskey = new System.Windows.Forms.TextBox(); - this.label37 = new System.Windows.Forms.Label(); - this.label33 = new System.Windows.Forms.Label(); - this.label34 = new System.Windows.Forms.Label(); - this.label24 = new System.Windows.Forms.Label(); - this.label23 = new System.Windows.Forms.Label(); - this.label22 = new System.Windows.Forms.Label(); - this.label21 = new System.Windows.Forms.Label(); - this.label20 = new System.Windows.Forms.Label(); + this.RAESKEY = new System.Windows.Forms.TextBox(); + this.lblRENCRYPTION_LEVEL = new System.Windows.Forms.Label(); + this.lblRRTSCTS = new System.Windows.Forms.Label(); + this.lblRMAX_WINDOW = new System.Windows.Forms.Label(); + this.lblRMIN_FREQ = new System.Windows.Forms.Label(); + this.lblRMAX_FREQ = new System.Windows.Forms.Label(); + this.lblRNUM_CHANNELS = new System.Windows.Forms.Label(); + this.lblRDUTY_CYCLE = new System.Windows.Forms.Label(); + this.lblRLBT_RSSI = new System.Windows.Forms.Label(); this.RTI2 = new System.Windows.Forms.TextBox(); this.label9 = new System.Windows.Forms.Label(); this.label10 = new System.Windows.Forms.Label(); - this.BUT_SetPPMFailSafe = new MissionPlanner.Controls.MyButton(); + this.dlgSave = new System.Windows.Forms.SaveFileDialog(); + this.dlgOpen = new System.Windows.Forms.OpenFileDialog(); this.BUT_loadcustom = new MissionPlanner.Controls.MyButton(); this.BUT_resettodefault = new MissionPlanner.Controls.MyButton(); + this.btnRemoteLoadFromFile = new MissionPlanner.Controls.MyButton(); + this.btnRemoteSaveToFile = new MissionPlanner.Controls.MyButton(); + this.BUT_SetPPMFailSafeRemote = new MissionPlanner.Controls.MyButton(); + this.BUT_SetPPMFailSafe = new MissionPlanner.Controls.MyButton(); this.BUT_savesettings = new MissionPlanner.Controls.MyButton(); this.BUT_getcurrent = new MissionPlanner.Controls.MyButton(); this.BUT_upload = new MissionPlanner.Controls.MyButton(); this.BUT_Syncoptions = new MissionPlanner.Controls.MyButton(); + this.btnSaveToFile = new MissionPlanner.Controls.MyButton(); + this.btnLoadFromFile = new MissionPlanner.Controls.MyButton(); this.groupBoxLocal.SuspendLayout(); this.groupBoxRemote.SuspendLayout(); this.SuspendLayout(); @@ -237,7 +284,6 @@ private void InitializeComponent() resources.GetString("RMAX_WINDOW.Items29"), resources.GetString("RMAX_WINDOW.Items30")}); this.RMAX_WINDOW.Name = "RMAX_WINDOW"; - this.toolTip1.SetToolTip(this.RMAX_WINDOW, resources.GetString("RMAX_WINDOW.ToolTip")); // // RMAX_FREQ // @@ -344,7 +390,6 @@ private void InitializeComponent() resources.ApplyResources(this.RMAVLINK, "RMAVLINK"); this.RMAVLINK.FormattingEnabled = true; this.RMAVLINK.Name = "RMAVLINK"; - this.toolTip1.SetToolTip(this.RMAVLINK, resources.GetString("RMAVLINK.ToolTip")); // // RGPO1_1R_COUT // @@ -510,7 +555,6 @@ private void InitializeComponent() resources.GetString("MAX_WINDOW.Items29"), resources.GetString("MAX_WINDOW.Items30")}); this.MAX_WINDOW.Name = "MAX_WINDOW"; - this.toolTip1.SetToolTip(this.MAX_WINDOW, resources.GetString("MAX_WINDOW.ToolTip")); // // MAX_FREQ // @@ -742,6 +786,70 @@ private void InitializeComponent() this.OPPRESEND.Name = "OPPRESEND"; this.toolTip1.SetToolTip(this.OPPRESEND, resources.GetString("OPPRESEND.ToolTip")); // + // ENCRYPTION_LEVEL + // + this.ENCRYPTION_LEVEL.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + resources.ApplyResources(this.ENCRYPTION_LEVEL, "ENCRYPTION_LEVEL"); + this.ENCRYPTION_LEVEL.FormattingEnabled = true; + this.ENCRYPTION_LEVEL.Name = "ENCRYPTION_LEVEL"; + this.toolTip1.SetToolTip(this.ENCRYPTION_LEVEL, resources.GetString("ENCRYPTION_LEVEL.ToolTip")); + this.ENCRYPTION_LEVEL.SelectedValueChanged += new System.EventHandler(this.ENCRYPTION_LEVEL_CheckedChanged); + // + // RENCRYPTION_LEVEL + // + this.RENCRYPTION_LEVEL.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + resources.ApplyResources(this.RENCRYPTION_LEVEL, "RENCRYPTION_LEVEL"); + this.RENCRYPTION_LEVEL.FormattingEnabled = true; + this.RENCRYPTION_LEVEL.Name = "RENCRYPTION_LEVEL"; + this.toolTip1.SetToolTip(this.RENCRYPTION_LEVEL, resources.GetString("RENCRYPTION_LEVEL.ToolTip")); + this.RENCRYPTION_LEVEL.SelectedValueChanged += new System.EventHandler(this.RENCRYPTION_LEVEL_CheckedChanged); + // + // FSFRAMELOSS + // + this.FSFRAMELOSS.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + resources.ApplyResources(this.FSFRAMELOSS, "FSFRAMELOSS"); + this.FSFRAMELOSS.FormattingEnabled = true; + this.FSFRAMELOSS.Name = "FSFRAMELOSS"; + this.toolTip1.SetToolTip(this.FSFRAMELOSS, resources.GetString("FSFRAMELOSS.ToolTip")); + // + // RFSFRAMELOSS + // + this.RFSFRAMELOSS.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + resources.ApplyResources(this.RFSFRAMELOSS, "RFSFRAMELOSS"); + this.RFSFRAMELOSS.FormattingEnabled = true; + this.RFSFRAMELOSS.Name = "RFSFRAMELOSS"; + this.toolTip1.SetToolTip(this.RFSFRAMELOSS, resources.GetString("RFSFRAMELOSS.ToolTip")); + // + // GPO1_3SBUSIN + // + resources.ApplyResources(this.GPO1_3SBUSIN, "GPO1_3SBUSIN"); + this.GPO1_3SBUSIN.Name = "GPO1_3SBUSIN"; + // + // RGPO1_3SBUSIN + // + resources.ApplyResources(this.RGPO1_3SBUSIN, "RGPO1_3SBUSIN"); + this.RGPO1_3SBUSIN.Name = "RGPO1_3SBUSIN"; + // + // GPO1_3STATLED + // + resources.ApplyResources(this.GPO1_3STATLED, "GPO1_3STATLED"); + this.GPO1_3STATLED.Name = "GPO1_3STATLED"; + // + // GPO1_0TXEN485 + // + resources.ApplyResources(this.GPO1_0TXEN485, "GPO1_0TXEN485"); + this.GPO1_0TXEN485.Name = "GPO1_0TXEN485"; + // + // RGPO1_0TXEN485 + // + resources.ApplyResources(this.RGPO1_0TXEN485, "RGPO1_0TXEN485"); + this.RGPO1_0TXEN485.Name = "RGPO1_0TXEN485"; + // + // RGPO1_3STATLED + // + resources.ApplyResources(this.RGPO1_3STATLED, "RGPO1_3STATLED"); + this.RGPO1_3STATLED.Name = "RGPO1_3STATLED"; + // // RTI // resources.ApplyResources(this.RTI, "RTI"); @@ -766,8 +874,10 @@ private void InitializeComponent() // // lbl_status // + this.lbl_status.BackColor = System.Drawing.Color.Transparent; resources.ApplyResources(this.lbl_status, "lbl_status"); this.lbl_status.Name = "lbl_status"; + this.lbl_status.UseMnemonic = false; // // ATI3 // @@ -777,6 +887,30 @@ private void InitializeComponent() // // groupBoxLocal // + this.groupBoxLocal.Controls.Add(this.btnLoadFromFile); + this.groupBoxLocal.Controls.Add(this.btnSaveToFile); + this.groupBoxLocal.Controls.Add(this.label54); + this.groupBoxLocal.Controls.Add(this.FSFRAMELOSS); + this.groupBoxLocal.Controls.Add(this.GPO1_3AUXOUT); + this.groupBoxLocal.Controls.Add(this.lblGPO1_3AUXOUT); + this.groupBoxLocal.Controls.Add(this.GPI1_2AUXIN); + this.groupBoxLocal.Controls.Add(this.lblGPI1_2AUXIN); + this.groupBoxLocal.Controls.Add(this.lblGPIO1_1FUNC); + this.groupBoxLocal.Controls.Add(this.GPIO1_1FUNC); + this.groupBoxLocal.Controls.Add(this.ENCRYPTION_LEVEL); + this.groupBoxLocal.Controls.Add(this.GPO1_0TXEN485); + this.groupBoxLocal.Controls.Add(this.lblGPO1_0TXEN485); + this.groupBoxLocal.Controls.Add(this.GPO1_3STATLED); + this.groupBoxLocal.Controls.Add(this.lblGPO1_3STATLED); + this.groupBoxLocal.Controls.Add(this.GPO1_3SBUSOUT); + this.groupBoxLocal.Controls.Add(this.label49); + this.groupBoxLocal.Controls.Add(this.BUT_SetPPMFailSafe); + this.groupBoxLocal.Controls.Add(this.txtCountry); + this.groupBoxLocal.Controls.Add(this.label45); + this.groupBoxLocal.Controls.Add(this.RATE_FREQBAND); + this.groupBoxLocal.Controls.Add(this.lblSBUSOUT); + this.groupBoxLocal.Controls.Add(this.lblSBUSIN); + this.groupBoxLocal.Controls.Add(this.GPO1_3SBUSIN); this.groupBoxLocal.Controls.Add(this.btnRandom); this.groupBoxLocal.Controls.Add(this.lblRX_ENCAP_METHOD); this.groupBoxLocal.Controls.Add(this.RX_ENCAP_METHOD); @@ -786,9 +920,9 @@ private void InitializeComponent() this.groupBoxLocal.Controls.Add(this.lblNODEID); this.groupBoxLocal.Controls.Add(this.DESTID); this.groupBoxLocal.Controls.Add(this.NODEID); - this.groupBoxLocal.Controls.Add(this.label40); + this.groupBoxLocal.Controls.Add(this.lblGPO1_1R_COUT); this.groupBoxLocal.Controls.Add(this.GPO1_1R_COUT); - this.groupBoxLocal.Controls.Add(this.label39); + this.groupBoxLocal.Controls.Add(this.lblGPI1_1R_CIN); this.groupBoxLocal.Controls.Add(this.GPI1_1R_CIN); this.groupBoxLocal.Controls.Add(this.MAVLINK); this.groupBoxLocal.Controls.Add(this.label2); @@ -798,14 +932,14 @@ private void InitializeComponent() this.groupBoxLocal.Controls.Add(this.AIR_SPEED); this.groupBoxLocal.Controls.Add(this.label3); this.groupBoxLocal.Controls.Add(this.NETID); - this.groupBoxLocal.Controls.Add(this.label4); + this.groupBoxLocal.Controls.Add(this.lblNETID); this.groupBoxLocal.Controls.Add(this.TXPOWER); - this.groupBoxLocal.Controls.Add(this.label5); + this.groupBoxLocal.Controls.Add(this.lblTXPOWER); this.groupBoxLocal.Controls.Add(this.ECC); - this.groupBoxLocal.Controls.Add(this.label6); - this.groupBoxLocal.Controls.Add(this.label8); + this.groupBoxLocal.Controls.Add(this.lblECC); + this.groupBoxLocal.Controls.Add(this.lblOPPRESEND); this.groupBoxLocal.Controls.Add(this.OPPRESEND); - this.groupBoxLocal.Controls.Add(this.label7); + this.groupBoxLocal.Controls.Add(this.lblMAVLINK); this.groupBoxLocal.Controls.Add(this.lblANT_MODE); this.groupBoxLocal.Controls.Add(this.ANT_MODE); this.groupBoxLocal.Controls.Add(this.lblSER_BRK_DETMS); @@ -816,25 +950,24 @@ private void InitializeComponent() this.groupBoxLocal.Controls.Add(this.MAX_RETRIES); this.groupBoxLocal.Controls.Add(this.MAX_DATA); this.groupBoxLocal.Controls.Add(this.lblMAX_DATA); - this.groupBoxLocal.Controls.Add(this.ENCRYPTION_LEVEL); - this.groupBoxLocal.Controls.Add(this.label36); + this.groupBoxLocal.Controls.Add(this.lblENCRYPTION_LEVEL); this.groupBoxLocal.Controls.Add(this.label35); - this.groupBoxLocal.Controls.Add(this.txt_aeskey); + this.groupBoxLocal.Controls.Add(this.AESKEY); this.groupBoxLocal.Controls.Add(this.RTSCTS); - this.groupBoxLocal.Controls.Add(this.label19); + this.groupBoxLocal.Controls.Add(this.lblRTSCTS); this.groupBoxLocal.Controls.Add(this.linkLabel_mavlink); this.groupBoxLocal.Controls.Add(this.linkLabel_lowlatency); - this.groupBoxLocal.Controls.Add(this.label18); + this.groupBoxLocal.Controls.Add(this.lblMAX_WINDOW); this.groupBoxLocal.Controls.Add(this.MAX_WINDOW); - this.groupBoxLocal.Controls.Add(this.label13); + this.groupBoxLocal.Controls.Add(this.lblMIN_FREQ); this.groupBoxLocal.Controls.Add(this.MAX_FREQ); this.groupBoxLocal.Controls.Add(this.NUM_CHANNELS); - this.groupBoxLocal.Controls.Add(this.label17); + this.groupBoxLocal.Controls.Add(this.lblLBT_RSSI); this.groupBoxLocal.Controls.Add(this.LBT_RSSI); - this.groupBoxLocal.Controls.Add(this.label16); + this.groupBoxLocal.Controls.Add(this.lblDUTY_CYCLE); this.groupBoxLocal.Controls.Add(this.MIN_FREQ); - this.groupBoxLocal.Controls.Add(this.label15); - this.groupBoxLocal.Controls.Add(this.label14); + this.groupBoxLocal.Controls.Add(this.lblNUM_CHANNELS); + this.groupBoxLocal.Controls.Add(this.lblMAX_FREQ); this.groupBoxLocal.Controls.Add(this.DUTY_CYCLE); this.groupBoxLocal.Controls.Add(this.ATI2); this.groupBoxLocal.Controls.Add(this.ATI3); @@ -846,6 +979,93 @@ private void InitializeComponent() this.groupBoxLocal.Name = "groupBoxLocal"; this.groupBoxLocal.TabStop = false; // + // label54 + // + resources.ApplyResources(this.label54, "label54"); + this.label54.Name = "label54"; + // + // GPO1_3AUXOUT + // + resources.ApplyResources(this.GPO1_3AUXOUT, "GPO1_3AUXOUT"); + this.GPO1_3AUXOUT.Name = "GPO1_3AUXOUT"; + // + // lblGPO1_3AUXOUT + // + resources.ApplyResources(this.lblGPO1_3AUXOUT, "lblGPO1_3AUXOUT"); + this.lblGPO1_3AUXOUT.Name = "lblGPO1_3AUXOUT"; + // + // GPI1_2AUXIN + // + resources.ApplyResources(this.GPI1_2AUXIN, "GPI1_2AUXIN"); + this.GPI1_2AUXIN.Name = "GPI1_2AUXIN"; + // + // lblGPI1_2AUXIN + // + resources.ApplyResources(this.lblGPI1_2AUXIN, "lblGPI1_2AUXIN"); + this.lblGPI1_2AUXIN.Name = "lblGPI1_2AUXIN"; + // + // lblGPIO1_1FUNC + // + resources.ApplyResources(this.lblGPIO1_1FUNC, "lblGPIO1_1FUNC"); + this.lblGPIO1_1FUNC.Name = "lblGPIO1_1FUNC"; + // + // GPIO1_1FUNC + // + this.GPIO1_1FUNC.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + resources.ApplyResources(this.GPIO1_1FUNC, "GPIO1_1FUNC"); + this.GPIO1_1FUNC.FormattingEnabled = true; + this.GPIO1_1FUNC.Name = "GPIO1_1FUNC"; + // + // lblGPO1_0TXEN485 + // + resources.ApplyResources(this.lblGPO1_0TXEN485, "lblGPO1_0TXEN485"); + this.lblGPO1_0TXEN485.Name = "lblGPO1_0TXEN485"; + // + // lblGPO1_3STATLED + // + resources.ApplyResources(this.lblGPO1_3STATLED, "lblGPO1_3STATLED"); + this.lblGPO1_3STATLED.Name = "lblGPO1_3STATLED"; + // + // GPO1_3SBUSOUT + // + this.GPO1_3SBUSOUT.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + resources.ApplyResources(this.GPO1_3SBUSOUT, "GPO1_3SBUSOUT"); + this.GPO1_3SBUSOUT.FormattingEnabled = true; + this.GPO1_3SBUSOUT.Name = "GPO1_3SBUSOUT"; + // + // label49 + // + resources.ApplyResources(this.label49, "label49"); + this.label49.Name = "label49"; + // + // txtCountry + // + resources.ApplyResources(this.txtCountry, "txtCountry"); + this.txtCountry.Name = "txtCountry"; + this.txtCountry.ReadOnly = true; + // + // label45 + // + resources.ApplyResources(this.label45, "label45"); + this.label45.Name = "label45"; + // + // RATE_FREQBAND + // + this.RATE_FREQBAND.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + resources.ApplyResources(this.RATE_FREQBAND, "RATE_FREQBAND"); + this.RATE_FREQBAND.FormattingEnabled = true; + this.RATE_FREQBAND.Name = "RATE_FREQBAND"; + // + // lblSBUSOUT + // + resources.ApplyResources(this.lblSBUSOUT, "lblSBUSOUT"); + this.lblSBUSOUT.Name = "lblSBUSOUT"; + // + // lblSBUSIN + // + resources.ApplyResources(this.lblSBUSIN, "lblSBUSIN"); + this.lblSBUSIN.Name = "lblSBUSIN"; + // // btnRandom // resources.ApplyResources(this.btnRandom, "btnRandom"); @@ -901,15 +1121,15 @@ private void InitializeComponent() this.NODEID.FormattingEnabled = true; this.NODEID.Name = "NODEID"; // - // label40 + // lblGPO1_1R_COUT // - resources.ApplyResources(this.label40, "label40"); - this.label40.Name = "label40"; + resources.ApplyResources(this.lblGPO1_1R_COUT, "lblGPO1_1R_COUT"); + this.lblGPO1_1R_COUT.Name = "lblGPO1_1R_COUT"; // - // label39 + // lblGPI1_1R_CIN // - resources.ApplyResources(this.label39, "label39"); - this.label39.Name = "label39"; + resources.ApplyResources(this.lblGPI1_1R_CIN, "lblGPI1_1R_CIN"); + this.lblGPI1_1R_CIN.Name = "lblGPI1_1R_CIN"; // // label2 // @@ -932,30 +1152,30 @@ private void InitializeComponent() resources.ApplyResources(this.label3, "label3"); this.label3.Name = "label3"; // - // label4 + // lblNETID // - resources.ApplyResources(this.label4, "label4"); - this.label4.Name = "label4"; + resources.ApplyResources(this.lblNETID, "lblNETID"); + this.lblNETID.Name = "lblNETID"; // - // label5 + // lblTXPOWER // - resources.ApplyResources(this.label5, "label5"); - this.label5.Name = "label5"; + resources.ApplyResources(this.lblTXPOWER, "lblTXPOWER"); + this.lblTXPOWER.Name = "lblTXPOWER"; // - // label6 + // lblECC // - resources.ApplyResources(this.label6, "label6"); - this.label6.Name = "label6"; + resources.ApplyResources(this.lblECC, "lblECC"); + this.lblECC.Name = "lblECC"; // - // label8 + // lblOPPRESEND // - resources.ApplyResources(this.label8, "label8"); - this.label8.Name = "label8"; + resources.ApplyResources(this.lblOPPRESEND, "lblOPPRESEND"); + this.lblOPPRESEND.Name = "lblOPPRESEND"; // - // label7 + // lblMAVLINK // - resources.ApplyResources(this.label7, "label7"); - this.label7.Name = "label7"; + resources.ApplyResources(this.lblMAVLINK, "lblMAVLINK"); + this.lblMAVLINK.Name = "lblMAVLINK"; // // lblANT_MODE // @@ -1017,75 +1237,67 @@ private void InitializeComponent() resources.ApplyResources(this.lblMAX_DATA, "lblMAX_DATA"); this.lblMAX_DATA.Name = "lblMAX_DATA"; // - // ENCRYPTION_LEVEL - // - resources.ApplyResources(this.ENCRYPTION_LEVEL, "ENCRYPTION_LEVEL"); - this.ENCRYPTION_LEVEL.Name = "ENCRYPTION_LEVEL"; - this.ENCRYPTION_LEVEL.CheckedChanged += new System.EventHandler(this.ENCRYPTION_LEVEL_CheckedChanged); + // lblENCRYPTION_LEVEL // - // label36 - // - resources.ApplyResources(this.label36, "label36"); - this.label36.Name = "label36"; + resources.ApplyResources(this.lblENCRYPTION_LEVEL, "lblENCRYPTION_LEVEL"); + this.lblENCRYPTION_LEVEL.Name = "lblENCRYPTION_LEVEL"; // // label35 // resources.ApplyResources(this.label35, "label35"); this.label35.Name = "label35"; // - // txt_aeskey + // txt_aeskeyraeskey // - resources.ApplyResources(this.txt_aeskey, "txt_aeskey"); - this.txt_aeskey.Name = "txt_aeskey"; + resources.ApplyResources(this.AESKEY, "txt_aeskey"); + this.AESKEY.Name = "AESKEY"; // - // label19 + // lblRTSCTS // - resources.ApplyResources(this.label19, "label19"); - this.label19.Name = "label19"; + resources.ApplyResources(this.lblRTSCTS, "lblRTSCTS"); + this.lblRTSCTS.Name = "lblRTSCTS"; // // linkLabel_mavlink // resources.ApplyResources(this.linkLabel_mavlink, "linkLabel_mavlink"); this.linkLabel_mavlink.Name = "linkLabel_mavlink"; this.linkLabel_mavlink.TabStop = true; - this.linkLabel_mavlink.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabel_mavlink_LinkClicked); // // linkLabel_lowlatency // resources.ApplyResources(this.linkLabel_lowlatency, "linkLabel_lowlatency"); this.linkLabel_lowlatency.Name = "linkLabel_lowlatency"; this.linkLabel_lowlatency.TabStop = true; - this.linkLabel_lowlatency.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabel_lowlatency_LinkClicked); // - // label18 + // lblMAX_WINDOW // - resources.ApplyResources(this.label18, "label18"); - this.label18.Name = "label18"; + resources.ApplyResources(this.lblMAX_WINDOW, "lblMAX_WINDOW"); + this.lblMAX_WINDOW.Name = "lblMAX_WINDOW"; // - // label13 + // lblMIN_FREQ // - resources.ApplyResources(this.label13, "label13"); - this.label13.Name = "label13"; + resources.ApplyResources(this.lblMIN_FREQ, "lblMIN_FREQ"); + this.lblMIN_FREQ.Name = "lblMIN_FREQ"; // - // label17 + // lblLBT_RSSI // - resources.ApplyResources(this.label17, "label17"); - this.label17.Name = "label17"; + resources.ApplyResources(this.lblLBT_RSSI, "lblLBT_RSSI"); + this.lblLBT_RSSI.Name = "lblLBT_RSSI"; // - // label16 + // lblDUTY_CYCLE // - resources.ApplyResources(this.label16, "label16"); - this.label16.Name = "label16"; + resources.ApplyResources(this.lblDUTY_CYCLE, "lblDUTY_CYCLE"); + this.lblDUTY_CYCLE.Name = "lblDUTY_CYCLE"; // - // label15 + // lblNUM_CHANNELS // - resources.ApplyResources(this.label15, "label15"); - this.label15.Name = "label15"; + resources.ApplyResources(this.lblNUM_CHANNELS, "lblNUM_CHANNELS"); + this.lblNUM_CHANNELS.Name = "lblNUM_CHANNELS"; // - // label14 + // lblMAX_FREQ // - resources.ApplyResources(this.label14, "label14"); - this.label14.Name = "label14"; + resources.ApplyResources(this.lblMAX_FREQ, "lblMAX_FREQ"); + this.lblMAX_FREQ.Name = "lblMAX_FREQ"; // // ATI2 // @@ -1095,6 +1307,30 @@ private void InitializeComponent() // // groupBoxRemote // + this.groupBoxRemote.Controls.Add(this.btnRemoteLoadFromFile); + this.groupBoxRemote.Controls.Add(this.btnRemoteSaveToFile); + this.groupBoxRemote.Controls.Add(this.lblRFSFRAMELOSS); + this.groupBoxRemote.Controls.Add(this.RFSFRAMELOSS); + this.groupBoxRemote.Controls.Add(this.RGPO1_3AUXOUT); + this.groupBoxRemote.Controls.Add(this.lblRGPO1_3AUXOUT); + this.groupBoxRemote.Controls.Add(this.RGPI1_2AUXIN); + this.groupBoxRemote.Controls.Add(this.lblRGPI1_2AUXIN); + this.groupBoxRemote.Controls.Add(this.lblRGPIO1_1FUNC); + this.groupBoxRemote.Controls.Add(this.RGPIO1_1FUNC); + this.groupBoxRemote.Controls.Add(this.RENCRYPTION_LEVEL); + this.groupBoxRemote.Controls.Add(this.RGPO1_0TXEN485); + this.groupBoxRemote.Controls.Add(this.lblRGPO1_0TXEN485); + this.groupBoxRemote.Controls.Add(this.RGPO1_3STATLED); + this.groupBoxRemote.Controls.Add(this.lblRGPO1_3STATLED); + this.groupBoxRemote.Controls.Add(this.RGPO1_3SBUSOUT); + this.groupBoxRemote.Controls.Add(this.BUT_SetPPMFailSafeRemote); + this.groupBoxRemote.Controls.Add(this.label50); + this.groupBoxRemote.Controls.Add(this.txtRCountry); + this.groupBoxRemote.Controls.Add(this.lblRSBUSOUT); + this.groupBoxRemote.Controls.Add(this.lblRSBUSIN); + this.groupBoxRemote.Controls.Add(this.RGPO1_3SBUSIN); + this.groupBoxRemote.Controls.Add(this.label46); + this.groupBoxRemote.Controls.Add(this.RRATE_FREQBAND); this.groupBoxRemote.Controls.Add(this.lblRRX_ENCAP_METHOD); this.groupBoxRemote.Controls.Add(this.RRX_ENCAP_METHOD); this.groupBoxRemote.Controls.Add(this.lblRTX_ENCAP_METHOD); @@ -1103,23 +1339,23 @@ private void InitializeComponent() this.groupBoxRemote.Controls.Add(this.lblRNODEID); this.groupBoxRemote.Controls.Add(this.RDESTID); this.groupBoxRemote.Controls.Add(this.RNODEID); - this.groupBoxRemote.Controls.Add(this.label41); + this.groupBoxRemote.Controls.Add(this.lblRGPO1_1R_COUT); this.groupBoxRemote.Controls.Add(this.RMAVLINK); this.groupBoxRemote.Controls.Add(this.RGPO1_1R_COUT); this.groupBoxRemote.Controls.Add(this.RFORMAT); - this.groupBoxRemote.Controls.Add(this.label42); + this.groupBoxRemote.Controls.Add(this.lblRGPI1_1R_CIN); this.groupBoxRemote.Controls.Add(this.RGPI1_1R_CIN); this.groupBoxRemote.Controls.Add(this.RSERIAL_SPEED); this.groupBoxRemote.Controls.Add(this.RAIR_SPEED); this.groupBoxRemote.Controls.Add(this.RNETID); - this.groupBoxRemote.Controls.Add(this.label25); + this.groupBoxRemote.Controls.Add(this.lblROPPRESEND); this.groupBoxRemote.Controls.Add(this.RTXPOWER); - this.groupBoxRemote.Controls.Add(this.label26); + this.groupBoxRemote.Controls.Add(this.lblRMAVLINK); this.groupBoxRemote.Controls.Add(this.RECC); - this.groupBoxRemote.Controls.Add(this.label27); - this.groupBoxRemote.Controls.Add(this.label28); + this.groupBoxRemote.Controls.Add(this.lblRECC); + this.groupBoxRemote.Controls.Add(this.lblRTXPOWER); this.groupBoxRemote.Controls.Add(this.ROPPRESEND); - this.groupBoxRemote.Controls.Add(this.label29); + this.groupBoxRemote.Controls.Add(this.lblRNETID); this.groupBoxRemote.Controls.Add(this.label32); this.groupBoxRemote.Controls.Add(this.label30); this.groupBoxRemote.Controls.Add(this.label31); @@ -1134,23 +1370,22 @@ private void InitializeComponent() this.groupBoxRemote.Controls.Add(this.RMAX_DATA); this.groupBoxRemote.Controls.Add(this.lblRMAX_DATA); this.groupBoxRemote.Controls.Add(this.label38); - this.groupBoxRemote.Controls.Add(this.RENCRYPTION_LEVEL); - this.groupBoxRemote.Controls.Add(this.txt_Raeskey); - this.groupBoxRemote.Controls.Add(this.label37); + this.groupBoxRemote.Controls.Add(this.RAESKEY); + this.groupBoxRemote.Controls.Add(this.lblRENCRYPTION_LEVEL); this.groupBoxRemote.Controls.Add(this.RRTSCTS); - this.groupBoxRemote.Controls.Add(this.label33); - this.groupBoxRemote.Controls.Add(this.label34); + this.groupBoxRemote.Controls.Add(this.lblRRTSCTS); + this.groupBoxRemote.Controls.Add(this.lblRMAX_WINDOW); this.groupBoxRemote.Controls.Add(this.RMAX_WINDOW); - this.groupBoxRemote.Controls.Add(this.label24); + this.groupBoxRemote.Controls.Add(this.lblRMIN_FREQ); this.groupBoxRemote.Controls.Add(this.RMAX_FREQ); this.groupBoxRemote.Controls.Add(this.RNUM_CHANNELS); this.groupBoxRemote.Controls.Add(this.RDUTY_CYCLE); this.groupBoxRemote.Controls.Add(this.RLBT_RSSI); this.groupBoxRemote.Controls.Add(this.RMIN_FREQ); - this.groupBoxRemote.Controls.Add(this.label23); - this.groupBoxRemote.Controls.Add(this.label22); - this.groupBoxRemote.Controls.Add(this.label21); - this.groupBoxRemote.Controls.Add(this.label20); + this.groupBoxRemote.Controls.Add(this.lblRMAX_FREQ); + this.groupBoxRemote.Controls.Add(this.lblRNUM_CHANNELS); + this.groupBoxRemote.Controls.Add(this.lblRDUTY_CYCLE); + this.groupBoxRemote.Controls.Add(this.lblRLBT_RSSI); this.groupBoxRemote.Controls.Add(this.RTI2); this.groupBoxRemote.Controls.Add(this.label9); this.groupBoxRemote.Controls.Add(this.RTI); @@ -1158,6 +1393,93 @@ private void InitializeComponent() this.groupBoxRemote.Name = "groupBoxRemote"; this.groupBoxRemote.TabStop = false; // + // lblRFSFRAMELOSS + // + resources.ApplyResources(this.lblRFSFRAMELOSS, "lblRFSFRAMELOSS"); + this.lblRFSFRAMELOSS.Name = "lblRFSFRAMELOSS"; + // + // RGPO1_3AUXOUT + // + resources.ApplyResources(this.RGPO1_3AUXOUT, "RGPO1_3AUXOUT"); + this.RGPO1_3AUXOUT.Name = "RGPO1_3AUXOUT"; + // + // lblRGPO1_3AUXOUT + // + resources.ApplyResources(this.lblRGPO1_3AUXOUT, "lblRGPO1_3AUXOUT"); + this.lblRGPO1_3AUXOUT.Name = "lblRGPO1_3AUXOUT"; + // + // RGPI1_2AUXIN + // + resources.ApplyResources(this.RGPI1_2AUXIN, "RGPI1_2AUXIN"); + this.RGPI1_2AUXIN.Name = "RGPI1_2AUXIN"; + // + // lblRGPI1_2AUXIN + // + resources.ApplyResources(this.lblRGPI1_2AUXIN, "lblRGPI1_2AUXIN"); + this.lblRGPI1_2AUXIN.Name = "lblRGPI1_2AUXIN"; + // + // lblRGPIO1_1FUNC + // + resources.ApplyResources(this.lblRGPIO1_1FUNC, "lblRGPIO1_1FUNC"); + this.lblRGPIO1_1FUNC.Name = "lblRGPIO1_1FUNC"; + // + // RGPIO1_1FUNC + // + this.RGPIO1_1FUNC.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + resources.ApplyResources(this.RGPIO1_1FUNC, "RGPIO1_1FUNC"); + this.RGPIO1_1FUNC.FormattingEnabled = true; + this.RGPIO1_1FUNC.Name = "RGPIO1_1FUNC"; + // + // lblRGPO1_0TXEN485 + // + resources.ApplyResources(this.lblRGPO1_0TXEN485, "lblRGPO1_0TXEN485"); + this.lblRGPO1_0TXEN485.Name = "lblRGPO1_0TXEN485"; + // + // lblRGPO1_3STATLED + // + resources.ApplyResources(this.lblRGPO1_3STATLED, "lblRGPO1_3STATLED"); + this.lblRGPO1_3STATLED.Name = "lblRGPO1_3STATLED"; + // + // RGPO1_3SBUSOUT + // + this.RGPO1_3SBUSOUT.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + resources.ApplyResources(this.RGPO1_3SBUSOUT, "RGPO1_3SBUSOUT"); + this.RGPO1_3SBUSOUT.FormattingEnabled = true; + this.RGPO1_3SBUSOUT.Name = "RGPO1_3SBUSOUT"; + // + // label50 + // + resources.ApplyResources(this.label50, "label50"); + this.label50.Name = "label50"; + // + // txtRCountry + // + resources.ApplyResources(this.txtRCountry, "txtRCountry"); + this.txtRCountry.Name = "txtRCountry"; + this.txtRCountry.ReadOnly = true; + // + // lblRSBUSOUT + // + resources.ApplyResources(this.lblRSBUSOUT, "lblRSBUSOUT"); + this.lblRSBUSOUT.Name = "lblRSBUSOUT"; + // + // lblRSBUSIN + // + resources.ApplyResources(this.lblRSBUSIN, "lblRSBUSIN"); + this.lblRSBUSIN.Name = "lblRSBUSIN"; + // + // label46 + // + resources.ApplyResources(this.label46, "label46"); + this.label46.Name = "label46"; + // + // RRATE_FREQBAND + // + this.RRATE_FREQBAND.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + resources.ApplyResources(this.RRATE_FREQBAND, "RRATE_FREQBAND"); + this.RRATE_FREQBAND.FormattingEnabled = true; + this.RRATE_FREQBAND.Name = "RRATE_FREQBAND"; + // // lblRRX_ENCAP_METHOD // resources.ApplyResources(this.lblRRX_ENCAP_METHOD, "lblRRX_ENCAP_METHOD"); @@ -1206,10 +1528,10 @@ private void InitializeComponent() resources.ApplyResources(this.RNODEID, "RNODEID"); this.RNODEID.Name = "RNODEID"; // - // label41 + // lblRGPO1_1R_COUT // - resources.ApplyResources(this.label41, "label41"); - this.label41.Name = "label41"; + resources.ApplyResources(this.lblRGPO1_1R_COUT, "lblRGPO1_1R_COUT"); + this.lblRGPO1_1R_COUT.Name = "lblRGPO1_1R_COUT"; // // RFORMAT // @@ -1217,35 +1539,35 @@ private void InitializeComponent() this.RFORMAT.Name = "RFORMAT"; this.RFORMAT.ReadOnly = true; // - // label42 + // lblRGPI1_1R_CIN // - resources.ApplyResources(this.label42, "label42"); - this.label42.Name = "label42"; + resources.ApplyResources(this.lblRGPI1_1R_CIN, "lblRGPI1_1R_CIN"); + this.lblRGPI1_1R_CIN.Name = "lblRGPI1_1R_CIN"; // - // label25 + // lblROPPRESEND // - resources.ApplyResources(this.label25, "label25"); - this.label25.Name = "label25"; + resources.ApplyResources(this.lblROPPRESEND, "lblROPPRESEND"); + this.lblROPPRESEND.Name = "lblROPPRESEND"; // - // label26 + // lblRMAVLINK // - resources.ApplyResources(this.label26, "label26"); - this.label26.Name = "label26"; + resources.ApplyResources(this.lblRMAVLINK, "lblRMAVLINK"); + this.lblRMAVLINK.Name = "lblRMAVLINK"; // - // label27 + // lblRECC // - resources.ApplyResources(this.label27, "label27"); - this.label27.Name = "label27"; + resources.ApplyResources(this.lblRECC, "lblRECC"); + this.lblRECC.Name = "lblRECC"; // - // label28 + // lblRTXPOWER // - resources.ApplyResources(this.label28, "label28"); - this.label28.Name = "label28"; + resources.ApplyResources(this.lblRTXPOWER, "lblRTXPOWER"); + this.lblRTXPOWER.Name = "lblRTXPOWER"; // - // label29 + // lblRNETID // - resources.ApplyResources(this.label29, "label29"); - this.label29.Name = "label29"; + resources.ApplyResources(this.lblRNETID, "lblRNETID"); + this.lblRNETID.Name = "lblRNETID"; // // label32 // @@ -1270,8 +1592,8 @@ private void InitializeComponent() // RANT_MODE // this.RANT_MODE.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.RANT_MODE.FormattingEnabled = true; resources.ApplyResources(this.RANT_MODE, "RANT_MODE"); + this.RANT_MODE.FormattingEnabled = true; this.RANT_MODE.Name = "RANT_MODE"; // // lblRSER_BRK_DETMS @@ -1327,56 +1649,50 @@ private void InitializeComponent() resources.ApplyResources(this.label38, "label38"); this.label38.Name = "label38"; // - // RENCRYPTION_LEVEL - // - resources.ApplyResources(this.RENCRYPTION_LEVEL, "RENCRYPTION_LEVEL"); - this.RENCRYPTION_LEVEL.Name = "RENCRYPTION_LEVEL"; - this.RENCRYPTION_LEVEL.CheckedChanged += new System.EventHandler(this.RENCRYPTION_LEVEL_CheckedChanged); - // // txt_Raeskey // - resources.ApplyResources(this.txt_Raeskey, "txt_Raeskey"); - this.txt_Raeskey.Name = "txt_Raeskey"; + resources.ApplyResources(this.RAESKEY, "txt_Raeskey"); + this.RAESKEY.Name = "RAESKEY"; // - // label37 + // lblRENCRYPTION_LEVEL // - resources.ApplyResources(this.label37, "label37"); - this.label37.Name = "label37"; + resources.ApplyResources(this.lblRENCRYPTION_LEVEL, "lblRENCRYPTION_LEVEL"); + this.lblRENCRYPTION_LEVEL.Name = "lblRENCRYPTION_LEVEL"; // - // label33 + // lblRRTSCTS // - resources.ApplyResources(this.label33, "label33"); - this.label33.Name = "label33"; + resources.ApplyResources(this.lblRRTSCTS, "lblRRTSCTS"); + this.lblRRTSCTS.Name = "lblRRTSCTS"; // - // label34 + // lblRMAX_WINDOW // - resources.ApplyResources(this.label34, "label34"); - this.label34.Name = "label34"; + resources.ApplyResources(this.lblRMAX_WINDOW, "lblRMAX_WINDOW"); + this.lblRMAX_WINDOW.Name = "lblRMAX_WINDOW"; // - // label24 + // lblRMIN_FREQ // - resources.ApplyResources(this.label24, "label24"); - this.label24.Name = "label24"; + resources.ApplyResources(this.lblRMIN_FREQ, "lblRMIN_FREQ"); + this.lblRMIN_FREQ.Name = "lblRMIN_FREQ"; // - // label23 + // lblRMAX_FREQ // - resources.ApplyResources(this.label23, "label23"); - this.label23.Name = "label23"; + resources.ApplyResources(this.lblRMAX_FREQ, "lblRMAX_FREQ"); + this.lblRMAX_FREQ.Name = "lblRMAX_FREQ"; // - // label22 + // lblRNUM_CHANNELS // - resources.ApplyResources(this.label22, "label22"); - this.label22.Name = "label22"; + resources.ApplyResources(this.lblRNUM_CHANNELS, "lblRNUM_CHANNELS"); + this.lblRNUM_CHANNELS.Name = "lblRNUM_CHANNELS"; // - // label21 + // lblRDUTY_CYCLE // - resources.ApplyResources(this.label21, "label21"); - this.label21.Name = "label21"; + resources.ApplyResources(this.lblRDUTY_CYCLE, "lblRDUTY_CYCLE"); + this.lblRDUTY_CYCLE.Name = "lblRDUTY_CYCLE"; // - // label20 + // lblRLBT_RSSI // - resources.ApplyResources(this.label20, "label20"); - this.label20.Name = "label20"; + resources.ApplyResources(this.lblRLBT_RSSI, "lblRLBT_RSSI"); + this.lblRLBT_RSSI.Name = "lblRLBT_RSSI"; // // RTI2 // @@ -1394,13 +1710,15 @@ private void InitializeComponent() resources.ApplyResources(this.label10, "label10"); this.label10.Name = "label10"; // - // BUT_SetPPMFailSafe + // dlgSave // - resources.ApplyResources(this.BUT_SetPPMFailSafe, "BUT_SetPPMFailSafe"); - this.BUT_SetPPMFailSafe.Name = "BUT_SetPPMFailSafe"; - this.toolTip1.SetToolTip(this.BUT_SetPPMFailSafe, resources.GetString("BUT_SetPPMFailSafe.ToolTip")); - this.BUT_SetPPMFailSafe.UseVisualStyleBackColor = true; - this.BUT_SetPPMFailSafe.Click += new System.EventHandler(this.BUT_SetPPMFailSafe_Click); + this.dlgSave.FileName = "*.ini"; + resources.ApplyResources(this.dlgSave, "dlgSave"); + // + // dlgOpen + // + this.dlgOpen.FileName = "*.ini"; + resources.ApplyResources(this.dlgOpen, "dlgOpen"); // // BUT_loadcustom // @@ -1416,6 +1734,36 @@ private void InitializeComponent() this.BUT_resettodefault.UseVisualStyleBackColor = true; this.BUT_resettodefault.Click += new System.EventHandler(this.BUT_resettodefault_Click); // + // btnRemoteLoadFromFile + // + resources.ApplyResources(this.btnRemoteLoadFromFile, "btnRemoteLoadFromFile"); + this.btnRemoteLoadFromFile.Name = "btnRemoteLoadFromFile"; + this.btnRemoteLoadFromFile.UseVisualStyleBackColor = true; + this.btnRemoteLoadFromFile.Click += new System.EventHandler(this.btnRemoteLoadFromFile_Click); + // + // btnRemoteSaveToFile + // + resources.ApplyResources(this.btnRemoteSaveToFile, "btnRemoteSaveToFile"); + this.btnRemoteSaveToFile.Name = "btnRemoteSaveToFile"; + this.btnRemoteSaveToFile.UseVisualStyleBackColor = true; + this.btnRemoteSaveToFile.Click += new System.EventHandler(this.btnRemoteSaveToFile_Click); + // + // BUT_SetPPMFailSafeRemote + // + resources.ApplyResources(this.BUT_SetPPMFailSafeRemote, "BUT_SetPPMFailSafeRemote"); + this.BUT_SetPPMFailSafeRemote.Name = "BUT_SetPPMFailSafeRemote"; + this.toolTip1.SetToolTip(this.BUT_SetPPMFailSafeRemote, resources.GetString("BUT_SetPPMFailSafeRemote.ToolTip")); + this.BUT_SetPPMFailSafeRemote.UseVisualStyleBackColor = true; + this.BUT_SetPPMFailSafeRemote.Click += new System.EventHandler(this.BUT_SetPPMFailSafeRemote_Click); + // + // BUT_SetPPMFailSafe + // + resources.ApplyResources(this.BUT_SetPPMFailSafe, "BUT_SetPPMFailSafe"); + this.BUT_SetPPMFailSafe.Name = "BUT_SetPPMFailSafe"; + this.toolTip1.SetToolTip(this.BUT_SetPPMFailSafe, resources.GetString("BUT_SetPPMFailSafe.ToolTip")); + this.BUT_SetPPMFailSafe.UseVisualStyleBackColor = true; + this.BUT_SetPPMFailSafe.Click += new System.EventHandler(this.BUT_SetPPMFailSafe_Click); + // // BUT_savesettings // resources.ApplyResources(this.BUT_savesettings, "BUT_savesettings"); @@ -1444,11 +1792,24 @@ private void InitializeComponent() this.BUT_Syncoptions.UseVisualStyleBackColor = true; this.BUT_Syncoptions.Click += new System.EventHandler(this.BUT_Syncoptions_Click); // + // btnSaveToFile + // + resources.ApplyResources(this.btnSaveToFile, "btnSaveToFile"); + this.btnSaveToFile.Name = "btnSaveToFile"; + this.btnSaveToFile.UseVisualStyleBackColor = true; + this.btnSaveToFile.Click += new System.EventHandler(this.btnSaveToFile_Click); + // + // btnLoadFromFile + // + resources.ApplyResources(this.btnLoadFromFile, "btnLoadFromFile"); + this.btnLoadFromFile.Name = "btnLoadFromFile"; + this.btnLoadFromFile.UseVisualStyleBackColor = true; + this.btnLoadFromFile.Click += new System.EventHandler(this.btnLoadFromFile_Click); + // // Sikradio // this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None; this.Controls.Add(this.Progressbar); - this.Controls.Add(this.BUT_SetPPMFailSafe); this.Controls.Add(this.BUT_loadcustom); this.Controls.Add(this.BUT_resettodefault); this.Controls.Add(this.linkLabel1); @@ -1469,147 +1830,194 @@ private void InitializeComponent() this.ResumeLayout(false); this.PerformLayout(); - } - - #endregion - - private Controls.MyButton BUT_upload; - private System.Windows.Forms.ProgressBar Progressbar; - private System.Windows.Forms.Label lbl_status; - private Controls.MyButton BUT_getcurrent; - private Controls.MyButton BUT_savesettings; - private System.Windows.Forms.ToolTip toolTip1; - private System.Windows.Forms.TextBox RTI; - private System.Windows.Forms.TextBox ATI; - private System.Windows.Forms.TextBox RSSI; - private System.Windows.Forms.Label label11; - private System.Windows.Forms.Label label12; - private Controls.MyButton BUT_Syncoptions; - private System.Windows.Forms.TextBox ATI3; - private System.Windows.Forms.GroupBox groupBoxLocal; - private System.Windows.Forms.GroupBox groupBoxRemote; - private System.Windows.Forms.Label label9; - private System.Windows.Forms.Label label10; - private System.Windows.Forms.LinkLabel linkLabel1; - private Controls.MyButton BUT_resettodefault; - private System.Windows.Forms.TextBox ATI2; - private System.Windows.Forms.TextBox RTI2; - private Controls.MyButton BUT_loadcustom; - private Controls.MyButton BUT_SetPPMFailSafe; - private System.Windows.Forms.Label lblRX_ENCAP_METHOD; - private System.Windows.Forms.ComboBox RX_ENCAP_METHOD; - private System.Windows.Forms.Label lblTX_ENCAP_METHOD; - private System.Windows.Forms.ComboBox TX_ENCAP_METHOD; - private System.Windows.Forms.Label lblDESTID; - private System.Windows.Forms.Label lblNODEID; - private System.Windows.Forms.ComboBox DESTID; - private System.Windows.Forms.ComboBox NODEID; - private System.Windows.Forms.Label label40; - private System.Windows.Forms.CheckBox GPO1_1R_COUT; - private System.Windows.Forms.Label label39; - private System.Windows.Forms.CheckBox GPI1_1R_CIN; - private System.Windows.Forms.ComboBox MAVLINK; - private System.Windows.Forms.Label label2; - private System.Windows.Forms.ComboBox SERIAL_SPEED; - private System.Windows.Forms.Label label1; - private System.Windows.Forms.TextBox FORMAT; - private System.Windows.Forms.ComboBox AIR_SPEED; - private System.Windows.Forms.Label label3; - private System.Windows.Forms.ComboBox NETID; - private System.Windows.Forms.Label label4; - private System.Windows.Forms.ComboBox TXPOWER; - private System.Windows.Forms.Label label5; - private System.Windows.Forms.CheckBox ECC; - private System.Windows.Forms.Label label6; - private System.Windows.Forms.Label label8; - private System.Windows.Forms.CheckBox OPPRESEND; - private System.Windows.Forms.Label label7; - private System.Windows.Forms.Label lblANT_MODE; - private System.Windows.Forms.ComboBox ANT_MODE; - private System.Windows.Forms.Label lblSER_BRK_DETMS; - private System.Windows.Forms.Label lblGLOBAL_RETRIES; - private System.Windows.Forms.Label lblMAX_RETRIES; - private System.Windows.Forms.ComboBox SER_BRK_DETMS; - private System.Windows.Forms.ComboBox GLOBAL_RETRIES; - private System.Windows.Forms.ComboBox MAX_RETRIES; - private System.Windows.Forms.ComboBox MAX_DATA; - private System.Windows.Forms.Label lblMAX_DATA; - private System.Windows.Forms.CheckBox ENCRYPTION_LEVEL; - private System.Windows.Forms.Label label36; - private System.Windows.Forms.Label label35; - private System.Windows.Forms.TextBox txt_aeskey; - private System.Windows.Forms.CheckBox RTSCTS; - private System.Windows.Forms.Label label19; - private System.Windows.Forms.LinkLabel linkLabel_mavlink; - private System.Windows.Forms.LinkLabel linkLabel_lowlatency; - private System.Windows.Forms.Label label18; - private System.Windows.Forms.ComboBox MAX_WINDOW; - private System.Windows.Forms.Label label13; - private System.Windows.Forms.ComboBox MAX_FREQ; - private System.Windows.Forms.ComboBox NUM_CHANNELS; - private System.Windows.Forms.Label label17; - private System.Windows.Forms.ComboBox LBT_RSSI; - private System.Windows.Forms.Label label16; - private System.Windows.Forms.ComboBox MIN_FREQ; - private System.Windows.Forms.Label label15; - private System.Windows.Forms.Label label14; - private System.Windows.Forms.ComboBox DUTY_CYCLE; - private System.Windows.Forms.Label lblRRX_ENCAP_METHOD; - private System.Windows.Forms.ComboBox RRX_ENCAP_METHOD; - private System.Windows.Forms.Label lblRTX_ENCAP_METHOD; - private System.Windows.Forms.ComboBox RTX_ENCAP_METHOD; - private System.Windows.Forms.Label lblRDESTID; - private System.Windows.Forms.Label lblRNODEID; - private System.Windows.Forms.ComboBox RDESTID; - private System.Windows.Forms.ComboBox RNODEID; - private System.Windows.Forms.Label label41; - private System.Windows.Forms.ComboBox RMAVLINK; - private System.Windows.Forms.CheckBox RGPO1_1R_COUT; - private System.Windows.Forms.TextBox RFORMAT; - private System.Windows.Forms.Label label42; - private System.Windows.Forms.CheckBox RGPI1_1R_CIN; - private System.Windows.Forms.ComboBox RSERIAL_SPEED; - private System.Windows.Forms.ComboBox RAIR_SPEED; - private System.Windows.Forms.ComboBox RNETID; - private System.Windows.Forms.Label label25; - private System.Windows.Forms.ComboBox RTXPOWER; - private System.Windows.Forms.Label label26; - private System.Windows.Forms.CheckBox RECC; - private System.Windows.Forms.Label label27; - private System.Windows.Forms.Label label28; - private System.Windows.Forms.CheckBox ROPPRESEND; - private System.Windows.Forms.Label label29; - private System.Windows.Forms.Label label32; - private System.Windows.Forms.Label label30; - private System.Windows.Forms.Label label31; - private System.Windows.Forms.Label lblRANT_MODE; - private System.Windows.Forms.ComboBox RANT_MODE; - private System.Windows.Forms.Label lblRSER_BRK_DETMS; - private System.Windows.Forms.Label lblRGLOBAL_RETRIES; - private System.Windows.Forms.Label lblRMAX_RETRIES; - private System.Windows.Forms.ComboBox RSER_BRK_DETMS; - private System.Windows.Forms.ComboBox RGLOBAL_RETRIES; - private System.Windows.Forms.ComboBox RMAX_RETRIES; - private System.Windows.Forms.ComboBox RMAX_DATA; - private System.Windows.Forms.Label lblRMAX_DATA; - private System.Windows.Forms.Label label38; - private System.Windows.Forms.CheckBox RENCRYPTION_LEVEL; - private System.Windows.Forms.TextBox txt_Raeskey; - private System.Windows.Forms.Label label37; - private System.Windows.Forms.CheckBox RRTSCTS; - private System.Windows.Forms.Label label33; - private System.Windows.Forms.Label label34; - private System.Windows.Forms.ComboBox RMAX_WINDOW; - private System.Windows.Forms.Label label24; - private System.Windows.Forms.ComboBox RMAX_FREQ; - private System.Windows.Forms.ComboBox RNUM_CHANNELS; - private System.Windows.Forms.ComboBox RDUTY_CYCLE; - private System.Windows.Forms.ComboBox RLBT_RSSI; - private System.Windows.Forms.ComboBox RMIN_FREQ; - private System.Windows.Forms.Label label23; - private System.Windows.Forms.Label label22; - private System.Windows.Forms.Label label21; - private System.Windows.Forms.Label label20; - private System.Windows.Forms.Button btnRandom; - } + } + + #endregion + + private Controls.MyButton BUT_upload; + private System.Windows.Forms.ProgressBar Progressbar; + private System.Windows.Forms.Label lbl_status; + private Controls.MyButton BUT_getcurrent; + private Controls.MyButton BUT_savesettings; + private System.Windows.Forms.ToolTip toolTip1; + private System.Windows.Forms.TextBox RTI; + private System.Windows.Forms.TextBox ATI; + private System.Windows.Forms.TextBox RSSI; + private System.Windows.Forms.Label label11; + private System.Windows.Forms.Label label12; + private Controls.MyButton BUT_Syncoptions; + private System.Windows.Forms.TextBox ATI3; + private System.Windows.Forms.GroupBox groupBoxLocal; + private System.Windows.Forms.GroupBox groupBoxRemote; + private System.Windows.Forms.Label label9; + private System.Windows.Forms.Label label10; + private System.Windows.Forms.LinkLabel linkLabel1; + private Controls.MyButton BUT_resettodefault; + private System.Windows.Forms.TextBox ATI2; + private System.Windows.Forms.TextBox RTI2; + private Controls.MyButton BUT_loadcustom; + private Controls.MyButton BUT_SetPPMFailSafe; + private System.Windows.Forms.Label lblRX_ENCAP_METHOD; + private System.Windows.Forms.ComboBox RX_ENCAP_METHOD; + private System.Windows.Forms.Label lblTX_ENCAP_METHOD; + private System.Windows.Forms.ComboBox TX_ENCAP_METHOD; + private System.Windows.Forms.Label lblDESTID; + private System.Windows.Forms.Label lblNODEID; + private System.Windows.Forms.ComboBox DESTID; + private System.Windows.Forms.ComboBox NODEID; + private System.Windows.Forms.Label lblGPO1_1R_COUT; + private System.Windows.Forms.CheckBox GPO1_1R_COUT; + private System.Windows.Forms.Label lblGPI1_1R_CIN; + private System.Windows.Forms.CheckBox GPI1_1R_CIN; + private System.Windows.Forms.ComboBox MAVLINK; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.ComboBox SERIAL_SPEED; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.TextBox FORMAT; + private System.Windows.Forms.ComboBox AIR_SPEED; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.ComboBox NETID; + private System.Windows.Forms.Label lblNETID; + private System.Windows.Forms.ComboBox TXPOWER; + private System.Windows.Forms.Label lblTXPOWER; + private System.Windows.Forms.CheckBox ECC; + private System.Windows.Forms.Label lblECC; + private System.Windows.Forms.Label lblOPPRESEND; + private System.Windows.Forms.CheckBox OPPRESEND; + private System.Windows.Forms.Label lblMAVLINK; + private System.Windows.Forms.Label lblANT_MODE; + private System.Windows.Forms.ComboBox ANT_MODE; + private System.Windows.Forms.Label lblSER_BRK_DETMS; + private System.Windows.Forms.Label lblGLOBAL_RETRIES; + private System.Windows.Forms.Label lblMAX_RETRIES; + private System.Windows.Forms.ComboBox SER_BRK_DETMS; + private System.Windows.Forms.ComboBox GLOBAL_RETRIES; + private System.Windows.Forms.ComboBox MAX_RETRIES; + private System.Windows.Forms.ComboBox MAX_DATA; + private System.Windows.Forms.Label lblMAX_DATA; + private System.Windows.Forms.Label lblENCRYPTION_LEVEL; + private System.Windows.Forms.Label label35; + private System.Windows.Forms.TextBox AESKEY; + private System.Windows.Forms.CheckBox RTSCTS; + private System.Windows.Forms.Label lblRTSCTS; + private System.Windows.Forms.LinkLabel linkLabel_mavlink; + private System.Windows.Forms.LinkLabel linkLabel_lowlatency; + private System.Windows.Forms.Label lblMAX_WINDOW; + private System.Windows.Forms.ComboBox MAX_WINDOW; + private System.Windows.Forms.Label lblMIN_FREQ; + private System.Windows.Forms.ComboBox MAX_FREQ; + private System.Windows.Forms.ComboBox NUM_CHANNELS; + private System.Windows.Forms.Label lblLBT_RSSI; + private System.Windows.Forms.ComboBox LBT_RSSI; + private System.Windows.Forms.Label lblDUTY_CYCLE; + private System.Windows.Forms.ComboBox MIN_FREQ; + private System.Windows.Forms.Label lblNUM_CHANNELS; + private System.Windows.Forms.Label lblMAX_FREQ; + private System.Windows.Forms.ComboBox DUTY_CYCLE; + private System.Windows.Forms.Label lblRRX_ENCAP_METHOD; + private System.Windows.Forms.ComboBox RRX_ENCAP_METHOD; + private System.Windows.Forms.Label lblRTX_ENCAP_METHOD; + private System.Windows.Forms.ComboBox RTX_ENCAP_METHOD; + private System.Windows.Forms.Label lblRDESTID; + private System.Windows.Forms.Label lblRNODEID; + private System.Windows.Forms.ComboBox RDESTID; + private System.Windows.Forms.ComboBox RNODEID; + private System.Windows.Forms.Label lblRGPO1_1R_COUT; + private System.Windows.Forms.ComboBox RMAVLINK; + private System.Windows.Forms.CheckBox RGPO1_1R_COUT; + private System.Windows.Forms.TextBox RFORMAT; + private System.Windows.Forms.Label lblRGPI1_1R_CIN; + private System.Windows.Forms.CheckBox RGPI1_1R_CIN; + private System.Windows.Forms.ComboBox RSERIAL_SPEED; + private System.Windows.Forms.ComboBox RAIR_SPEED; + private System.Windows.Forms.ComboBox RNETID; + private System.Windows.Forms.Label lblROPPRESEND; + private System.Windows.Forms.ComboBox RTXPOWER; + private System.Windows.Forms.Label lblRMAVLINK; + private System.Windows.Forms.CheckBox RECC; + private System.Windows.Forms.Label lblRECC; + private System.Windows.Forms.Label lblRTXPOWER; + private System.Windows.Forms.CheckBox ROPPRESEND; + private System.Windows.Forms.Label lblRNETID; + private System.Windows.Forms.Label label32; + private System.Windows.Forms.Label label30; + private System.Windows.Forms.Label label31; + private System.Windows.Forms.Label lblRANT_MODE; + private System.Windows.Forms.ComboBox RANT_MODE; + private System.Windows.Forms.Label lblRSER_BRK_DETMS; + private System.Windows.Forms.Label lblRGLOBAL_RETRIES; + private System.Windows.Forms.Label lblRMAX_RETRIES; + private System.Windows.Forms.ComboBox RSER_BRK_DETMS; + private System.Windows.Forms.ComboBox RGLOBAL_RETRIES; + private System.Windows.Forms.ComboBox RMAX_RETRIES; + private System.Windows.Forms.ComboBox RMAX_DATA; + private System.Windows.Forms.Label lblRMAX_DATA; + private System.Windows.Forms.Label label38; + private System.Windows.Forms.TextBox RAESKEY; + private System.Windows.Forms.Label lblRENCRYPTION_LEVEL; + private System.Windows.Forms.CheckBox RRTSCTS; + private System.Windows.Forms.Label lblRRTSCTS; + private System.Windows.Forms.Label lblRMAX_WINDOW; + private System.Windows.Forms.ComboBox RMAX_WINDOW; + private System.Windows.Forms.Label lblRMIN_FREQ; + private System.Windows.Forms.ComboBox RMAX_FREQ; + private System.Windows.Forms.ComboBox RNUM_CHANNELS; + private System.Windows.Forms.ComboBox RDUTY_CYCLE; + private System.Windows.Forms.ComboBox RLBT_RSSI; + private System.Windows.Forms.ComboBox RMIN_FREQ; + private System.Windows.Forms.Label lblRMAX_FREQ; + private System.Windows.Forms.Label lblRNUM_CHANNELS; + private System.Windows.Forms.Label lblRDUTY_CYCLE; + private System.Windows.Forms.Label lblRLBT_RSSI; + private System.Windows.Forms.Button btnRandom; + private System.Windows.Forms.Label lblSBUSIN; + private System.Windows.Forms.CheckBox GPO1_3SBUSIN; + private System.Windows.Forms.Label lblSBUSOUT; + private System.Windows.Forms.Label label45; + private System.Windows.Forms.ComboBox RATE_FREQBAND; + private System.Windows.Forms.Label label46; + private System.Windows.Forms.ComboBox RRATE_FREQBAND; + private System.Windows.Forms.Label lblRSBUSOUT; + private System.Windows.Forms.Label lblRSBUSIN; + private System.Windows.Forms.CheckBox RGPO1_3SBUSIN; + private System.Windows.Forms.TextBox txtCountry; + private System.Windows.Forms.Label label49; + private System.Windows.Forms.Label label50; + private System.Windows.Forms.TextBox txtRCountry; + private Controls.MyButton BUT_SetPPMFailSafeRemote; + private System.Windows.Forms.ComboBox GPO1_3SBUSOUT; + private System.Windows.Forms.ComboBox RGPO1_3SBUSOUT; + private System.Windows.Forms.CheckBox GPO1_0TXEN485; + private System.Windows.Forms.Label lblGPO1_0TXEN485; + private System.Windows.Forms.CheckBox GPO1_3STATLED; + private System.Windows.Forms.Label lblGPO1_3STATLED; + private System.Windows.Forms.CheckBox RGPO1_0TXEN485; + private System.Windows.Forms.Label lblRGPO1_0TXEN485; + private System.Windows.Forms.CheckBox RGPO1_3STATLED; + private System.Windows.Forms.Label lblRGPO1_3STATLED; + private System.Windows.Forms.ComboBox ENCRYPTION_LEVEL; + private System.Windows.Forms.ComboBox RENCRYPTION_LEVEL; + private System.Windows.Forms.Label lblGPIO1_1FUNC; + private System.Windows.Forms.ComboBox GPIO1_1FUNC; + private System.Windows.Forms.Label lblRGPIO1_1FUNC; + private System.Windows.Forms.ComboBox RGPIO1_1FUNC; + private System.Windows.Forms.CheckBox GPO1_3AUXOUT; + private System.Windows.Forms.Label lblGPO1_3AUXOUT; + private System.Windows.Forms.CheckBox GPI1_2AUXIN; + private System.Windows.Forms.Label lblGPI1_2AUXIN; + private System.Windows.Forms.Label label54; + private System.Windows.Forms.ComboBox FSFRAMELOSS; + private System.Windows.Forms.Label lblRFSFRAMELOSS; + private System.Windows.Forms.ComboBox RFSFRAMELOSS; + private System.Windows.Forms.CheckBox RGPO1_3AUXOUT; + private System.Windows.Forms.Label lblRGPO1_3AUXOUT; + private System.Windows.Forms.CheckBox RGPI1_2AUXIN; + private System.Windows.Forms.Label lblRGPI1_2AUXIN; + private Controls.MyButton btnSaveToFile; + private System.Windows.Forms.SaveFileDialog dlgSave; + private Controls.MyButton btnRemoteSaveToFile; + private Controls.MyButton btnLoadFromFile; + private System.Windows.Forms.OpenFileDialog dlgOpen; + private Controls.MyButton btnRemoteLoadFromFile; + } } \ No newline at end of file diff --git a/Radio/Sikradio.cs b/Radio/Sikradio.cs index 688fb4d8a6..e4d5b04610 100644 --- a/Radio/Sikradio.cs +++ b/Radio/Sikradio.cs @@ -1,6 +1,3 @@ -using log4net; -using MissionPlanner.Comms; -using MissionPlanner.Utilities; using System; using System.Collections.Generic; using System.Globalization; @@ -11,6 +8,13 @@ using System.Text.RegularExpressions; using System.Threading; using System.Windows.Forms; +using log4net; +using MissionPlanner.Comms; +using MissionPlanner.Controls; +using MissionPlanner.MsgBox; +using MissionPlanner.Radio; +using MissionPlanner.Utilities; +using Microsoft.VisualBasic; namespace MissionPlanner.Radio { @@ -20,23 +24,32 @@ public partial class Sikradio : UserControl, SikRadio.ISikRadioForm public delegate void ProgressEventHandler(double completed); - /// - /// An alternative function for getting the serial port. Can be left as null - /// if no alternative. - /// - public Func GetCommsSerialAlt; - private static readonly ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private bool beta; - private readonly string firmwarefile = Path.GetTempFileName(); + private string firmwarefile = Path.GetTempFileName(); private Dictionary _DefaultLocalEnabled = new Dictionary(); private Dictionary _DefaultCBObjects = new Dictionary(); RFD.RFD900.TSession _Session; ExtraParamControlsSet _LocalExtraParams; ExtraParamControlsSet _RemoteExtraParams; + RFDLib.GUI.Settings.TDynamicLabelEditorPairRegister _DynamicLabelEditorPairRegister; + + RFDLib.GUI.Settings.TLabelEditorPairRegister _LocalLabelEditorPairs = new RFDLib.GUI.Settings.TLabelEditorPairRegister(); + RFDLib.GUI.Settings.TLabelEditorPairRegister _RemoteLabelEditorPairs = new RFDLib.GUI.Settings.TLabelEditorPairRegister(); + Dictionary _KnownNameDescriptions = new Dictionary() + { + {"RSSI_IN_DBM", "RSSI in dBm"}, + {"AUXSER_SPEED", "Aux Baud"}, + {"AIR_FRAMELEN", "Air Frame Length"}, + }; + + RFD.RFD900.TSettings _LocalSettings, _RemoteSettings; + + public event Action DoDisconnectReconnect; + /* ATI5 S0: FORMAT=25 @@ -67,17 +80,15 @@ public Sikradio() _LocalExtraParams = new ExtraParamControlsSet(lblNODEID, NODEID, lblDESTID, DESTID, lblTX_ENCAP_METHOD, TX_ENCAP_METHOD, lblRX_ENCAP_METHOD, RX_ENCAP_METHOD, - lblMAX_DATA, MAX_DATA, - new Control[] {lblMAX_RETRIES, MAX_RETRIES, - lblGLOBAL_RETRIES, GLOBAL_RETRIES, lblSER_BRK_DETMS, SER_BRK_DETMS, - lblANT_MODE, ANT_MODE}, false); + lblMAX_DATA, MAX_DATA, lblMAX_RETRIES, MAX_RETRIES, + new Control[] { + lblGLOBAL_RETRIES, GLOBAL_RETRIES, lblSER_BRK_DETMS, SER_BRK_DETMS}, false); _RemoteExtraParams = new ExtraParamControlsSet(lblRNODEID, RNODEID, lblRDESTID, RDESTID, lblRTX_ENCAP_METHOD, RTX_ENCAP_METHOD, lblRRX_ENCAP_METHOD, RRX_ENCAP_METHOD, - lblRMAX_DATA, RMAX_DATA, - new Control[] {lblRMAX_RETRIES, RMAX_RETRIES, - lblRGLOBAL_RETRIES, RGLOBAL_RETRIES, lblRSER_BRK_DETMS, RSER_BRK_DETMS, - lblRANT_MODE, RANT_MODE}, true); + lblRMAX_DATA, RMAX_DATA, lblRMAX_RETRIES, RMAX_RETRIES, + new Control[] { + lblRGLOBAL_RETRIES, RGLOBAL_RETRIES, lblRSER_BRK_DETMS, RSER_BRK_DETMS}, true); // setup netid NETID.DataSource = Enumerable.Range(0, 500).ToArray(); @@ -117,9 +128,148 @@ public Sikradio() SaveDefaultCBObjects(MAX_WINDOW); SaveDefaultCBObjects(RMAX_WINDOW); + RFDLib.GUI.Settings.TDynamicLabelEditorPair SBUSIN = new RFDLib.GUI.Settings.TDynamicLabelEditorPair(lblSBUSIN, GPO1_3SBUSIN, + new RFDLib.GUI.Settings.TDynamicLabelEditorPair.TSettingNameLabelTextPair[] + { + new RFDLib.GUI.Settings.TDynamicLabelEditorPair.TSettingNameLabelTextPair("GPO1_3SBUSIN", "GPO1_3SBUSIN"), + new RFDLib.GUI.Settings.TDynamicLabelEditorPair.TSettingNameLabelTextPair("GPO1_1SBUSIN", "GPO1_1SBUSIN"), + }); + RFDLib.GUI.Settings.TDynamicLabelEditorPair RSBUSIN = new RFDLib.GUI.Settings.TDynamicLabelEditorPair(lblRSBUSIN, RGPO1_3SBUSIN, + new RFDLib.GUI.Settings.TDynamicLabelEditorPair.TSettingNameLabelTextPair[] + { + new RFDLib.GUI.Settings.TDynamicLabelEditorPair.TSettingNameLabelTextPair("RGPO1_3SBUSIN", "GPO1_3SBUSIN"), + new RFDLib.GUI.Settings.TDynamicLabelEditorPair.TSettingNameLabelTextPair("RGPO1_1SBUSIN", "GPO1_1SBUSIN"), + }); + RFDLib.GUI.Settings.TDynamicLabelEditorPair SBUSOUT = new RFDLib.GUI.Settings.TDynamicLabelEditorPair(lblSBUSOUT, GPO1_3SBUSOUT, + new RFDLib.GUI.Settings.TDynamicLabelEditorPair.TSettingNameLabelTextPair[] + { + new RFDLib.GUI.Settings.TDynamicLabelEditorPair.TSettingNameLabelTextPair("GPO1_3SBUSOUT", "GPO1_3SBUSOUT"), + new RFDLib.GUI.Settings.TDynamicLabelEditorPair.TSettingNameLabelTextPair("GPO1_1SBUSOUT", "GPO1_1SBUSOUT"), + }); + RFDLib.GUI.Settings.TDynamicLabelEditorPair RSBUSOUT = new RFDLib.GUI.Settings.TDynamicLabelEditorPair(lblRSBUSOUT, RGPO1_3SBUSOUT, + new RFDLib.GUI.Settings.TDynamicLabelEditorPair.TSettingNameLabelTextPair[] + { + new RFDLib.GUI.Settings.TDynamicLabelEditorPair.TSettingNameLabelTextPair("RGPO1_3SBUSOUT", "GPO1_3SBUSOUT"), + new RFDLib.GUI.Settings.TDynamicLabelEditorPair.TSettingNameLabelTextPair("RGPO1_1SBUSOUT", "GPO1_1SBUSOUT"), + }); + _DynamicLabelEditorPairRegister = new RFDLib.GUI.Settings.TDynamicLabelEditorPairRegister(new RFDLib.GUI.Settings.TDynamicLabelEditorPair[] + { + SBUSIN, RSBUSIN, SBUSOUT, RSBUSOUT, + }); + + _LocalLabelEditorPairs.Add(lblNODEID, NODEID, toolTip1); + _LocalLabelEditorPairs.Add(lblDESTID, DESTID, toolTip1); + _LocalLabelEditorPairs.Add(lblTX_ENCAP_METHOD, TX_ENCAP_METHOD, toolTip1); + _LocalLabelEditorPairs.Add(lblRX_ENCAP_METHOD, RX_ENCAP_METHOD, toolTip1); + _LocalLabelEditorPairs.Add(lblMAX_DATA, MAX_DATA, toolTip1); + _LocalLabelEditorPairs.Add(lblMAX_RETRIES, MAX_RETRIES, toolTip1); + _LocalLabelEditorPairs.Add(lblGLOBAL_RETRIES, GLOBAL_RETRIES, toolTip1); + _LocalLabelEditorPairs.Add(lblSER_BRK_DETMS, SER_BRK_DETMS, toolTip1); + _LocalLabelEditorPairs.Add(label54, FSFRAMELOSS, toolTip1); + _LocalLabelEditorPairs.Add(lblNETID, NETID, toolTip1); + _LocalLabelEditorPairs.Add(lblTXPOWER, TXPOWER, toolTip1); + _LocalLabelEditorPairs.Add(lblECC, ECC, toolTip1); + _LocalLabelEditorPairs.Add(lblMAVLINK, MAVLINK, toolTip1); + _LocalLabelEditorPairs.Add(lblOPPRESEND, OPPRESEND, toolTip1); + _LocalLabelEditorPairs.Add(lblGPI1_1R_CIN, GPI1_1R_CIN, toolTip1); + _LocalLabelEditorPairs.Add(lblGPO1_1R_COUT, GPO1_1R_COUT, toolTip1); + _LocalLabelEditorPairs.Add(lblGPO1_3STATLED, GPO1_3STATLED, toolTip1); + _LocalLabelEditorPairs.Add(lblGPI1_2AUXIN, GPI1_2AUXIN, toolTip1); + _LocalLabelEditorPairs.Add(lblGPO1_3AUXOUT, GPO1_3AUXOUT, toolTip1); + _LocalLabelEditorPairs.Add(lblMIN_FREQ, MIN_FREQ, toolTip1); + _LocalLabelEditorPairs.Add(lblMAX_FREQ, MAX_FREQ, toolTip1); + _LocalLabelEditorPairs.Add(lblNUM_CHANNELS, NUM_CHANNELS, toolTip1); + _LocalLabelEditorPairs.Add(lblDUTY_CYCLE, DUTY_CYCLE, toolTip1); + _LocalLabelEditorPairs.Add(lblLBT_RSSI, LBT_RSSI, toolTip1); + _LocalLabelEditorPairs.Add(lblRTSCTS, RTSCTS, toolTip1); + _LocalLabelEditorPairs.Add(lblMAX_WINDOW, MAX_WINDOW, toolTip1); + _LocalLabelEditorPairs.Add(lblENCRYPTION_LEVEL, ENCRYPTION_LEVEL, toolTip1); + _LocalLabelEditorPairs.Add(lblGPO1_0TXEN485, GPO1_0TXEN485, toolTip1); + _LocalLabelEditorPairs.Add(lblGPIO1_1FUNC, GPIO1_1FUNC, toolTip1); + + _RemoteLabelEditorPairs.Add(lblRNODEID, RNODEID, toolTip1); + _RemoteLabelEditorPairs.Add(lblRDESTID, RDESTID, toolTip1); + _RemoteLabelEditorPairs.Add(lblRTX_ENCAP_METHOD, RTX_ENCAP_METHOD, toolTip1); + _RemoteLabelEditorPairs.Add(lblRRX_ENCAP_METHOD, RRX_ENCAP_METHOD, toolTip1); + _RemoteLabelEditorPairs.Add(lblRMAX_DATA, RMAX_DATA, toolTip1); + _RemoteLabelEditorPairs.Add(lblRMAX_RETRIES, RMAX_RETRIES, toolTip1); + _RemoteLabelEditorPairs.Add(lblRGLOBAL_RETRIES, RGLOBAL_RETRIES, toolTip1); + _RemoteLabelEditorPairs.Add(lblRSER_BRK_DETMS, RSER_BRK_DETMS, toolTip1); + _RemoteLabelEditorPairs.Add(lblRFSFRAMELOSS, RFSFRAMELOSS, toolTip1); + _RemoteLabelEditorPairs.Add(lblRNETID, RNETID, toolTip1); + _RemoteLabelEditorPairs.Add(lblRTXPOWER, RTXPOWER, toolTip1); + _RemoteLabelEditorPairs.Add(lblRECC, RECC, toolTip1); + _RemoteLabelEditorPairs.Add(lblRMAVLINK, RMAVLINK, toolTip1); + _RemoteLabelEditorPairs.Add(lblROPPRESEND, ROPPRESEND, toolTip1); + _RemoteLabelEditorPairs.Add(lblRGPI1_1R_CIN, RGPI1_1R_CIN, toolTip1); + _RemoteLabelEditorPairs.Add(lblRGPO1_1R_COUT, RGPO1_1R_COUT, toolTip1); + _RemoteLabelEditorPairs.Add(lblRGPO1_3STATLED, RGPO1_3STATLED, toolTip1); + _RemoteLabelEditorPairs.Add(lblRGPI1_2AUXIN, RGPI1_2AUXIN, toolTip1); + _RemoteLabelEditorPairs.Add(lblRGPO1_3AUXOUT, RGPO1_3AUXOUT, toolTip1); + _RemoteLabelEditorPairs.Add(lblRMIN_FREQ, RMIN_FREQ, toolTip1); + _RemoteLabelEditorPairs.Add(lblRMAX_FREQ, RMAX_FREQ, toolTip1); + _RemoteLabelEditorPairs.Add(lblRNUM_CHANNELS, RNUM_CHANNELS, toolTip1); + _RemoteLabelEditorPairs.Add(lblRDUTY_CYCLE, RDUTY_CYCLE, toolTip1); + _RemoteLabelEditorPairs.Add(lblRLBT_RSSI, RLBT_RSSI, toolTip1); + _RemoteLabelEditorPairs.Add(lblRRTSCTS, RRTSCTS, toolTip1); + _RemoteLabelEditorPairs.Add(lblRMAX_WINDOW, RMAX_WINDOW, toolTip1); + _RemoteLabelEditorPairs.Add(lblRENCRYPTION_LEVEL, RENCRYPTION_LEVEL, toolTip1); + _RemoteLabelEditorPairs.Add(lblRGPO1_0TXEN485, RGPO1_0TXEN485, toolTip1); + _RemoteLabelEditorPairs.Add(lblRGPIO1_1FUNC, RGPIO1_1FUNC, toolTip1); + this.Disposed += DisposedEvtHdlr; } + static void getTelemPortWithRadio(ref ICommsSerial comPort) + { + // try telem1 + + comPort = new MAVLinkSerialPort(MainV2.comPort, (int)MAVLink.SERIAL_CONTROL_DEV.TELEM1); + + comPort.ReadTimeout = 4000; + + comPort.Open(); + } + + public static bool Connect(ref ICommsSerial Port) + { + try + { + if (MainV2.comPort.BaseStream.PortName.Contains("TCP")) + { + Port = new TcpSerial(); + Port.BaudRate = MainV2.comPort.BaseStream.BaudRate; + Port.ReadTimeout = 4000; + Port.Open(); + } + else + { + Port = new SerialPort(); + + if (MainV2.comPort.BaseStream.IsOpen) + { + getTelemPortWithRadio(ref Port); + } + else + { + Port.PortName = MainV2.comPort.BaseStream.PortName; + Port.BaudRate = MainV2.comPort.BaseStream.BaudRate; + } + + Port.ReadTimeout = 4000; + + Port.Open(); + } + + return true; + } + catch + { + return false; + } + } + + public void Connect() { var S = GetSession(); @@ -128,11 +278,12 @@ public void Connect() public void Disconnect() { var S = _Session; - if (S != null) + if ((S != null) && S.Port.IsOpen) { S.PutIntoTransparentMode(); } EndSession(); + MissionPlanner.Radio.ComPort.FinishedWithComPortForSiKRadio(); } void DisposedEvtHdlr(object sender, EventArgs e) @@ -178,63 +329,56 @@ private void RestoreAllDefaultCBObjects() } } - private bool getFirmware(Uploader.Board device, bool custom = false) + private bool getFirmware(Uploader.Board device, RFD.RFD900.RFD900 RFD900, bool custom = false) { if (custom) { - return getFirmwareLocal(device == Uploader.Board.DEVICE_ID_RFD900X); + return getFirmwareLocal(RFD900); } + + firmwarefile = Path.GetTempFileName(); - if (device == Uploader.Board.DEVICE_ID_HB1060) - { - if (beta) - { - return Download.getFilefromNet("https://firmware.ardupilot.org/SiK/beta/radio~hb1060.ihx", firmwarefile); - } - return Download.getFilefromNet("https://firmware.ardupilot.org/SiK/stable/radio~hb1060.ihx", - firmwarefile); - } if (device == Uploader.Board.DEVICE_ID_HM_TRP) { if (beta) { - return Download.getFilefromNet("https://firmware.ardupilot.org/SiK/beta/radio~hm_trp.ihx", firmwarefile); + return Download.getFilefromNet("http://firmware.ardupilot.org/SiK/beta/radio~hm_trp.ihx", firmwarefile); } - return Download.getFilefromNet("https://firmware.ardupilot.org/SiK/stable/radio~hm_trp.ihx", + return Download.getFilefromNet("http://firmware.ardupilot.org/SiK/stable/radio~hm_trp.ihx", firmwarefile); } if (device == Uploader.Board.DEVICE_ID_RFD900) { if (beta) { - return Download.getFilefromNet("https://firmware.ardupilot.org/SiK/beta/radio~rfd900.ihx", firmwarefile); + return Download.getFilefromNet("http://firmware.ardupilot.org/SiK/beta/radio~rfd900.ihx", firmwarefile); } - return Download.getFilefromNet("https://firmware.ardupilot.org/SiK/stable/radio~rfd900.ihx", firmwarefile); + return Download.getFilefromNet("http://firmware.ardupilot.org/SiK/stable/radio~rfd900.ihx", firmwarefile); } if (device == Uploader.Board.DEVICE_ID_RFD900A) { if (beta) { - return Download.getFilefromNet("https://firmware.ardupilot.org/SiK/beta/radio~rfd900a.ihx", + return Download.getFilefromNet("http://firmware.ardupilot.org/SiK/beta/radio~rfd900a.ihx", firmwarefile); } - return Download.getFilefromNet("https://firmware.ardupilot.org/SiK/stable/radio~rfd900a.ihx", firmwarefile); + return Download.getFilefromNet("http://firmware.ardupilot.org/SiK/stable/radio~rfd900a.ihx", firmwarefile); } if (device == Uploader.Board.DEVICE_ID_RFD900U) { if (beta) { - return Download.getFilefromNet("https://files.rfdesign.com.au/Files/firmware/MPSiK%20V2.6%20rfd900u.ihx", firmwarefile); + return Download.getFilefromNet("http://files.rfdesign.com.au/Files/firmware/MPSiK%20V2.6%20rfd900u.ihx", firmwarefile); } - return Download.getFilefromNet("https://files.rfdesign.com.au/Files/firmware/RFDSiK%20V1.9%20rfd900u.ihx", firmwarefile); + return Download.getFilefromNet("http://files.rfdesign.com.au/Files/firmware/RFDSiK%20V1.9%20rfd900u.ihx", firmwarefile); } if (device == Uploader.Board.DEVICE_ID_RFD900P) { if (beta) { - return Download.getFilefromNet("https://files.rfdesign.com.au/Files/firmware/MPSiK%20V2.6%20rfd900p.ihx", firmwarefile); + return Download.getFilefromNet("http://files.rfdesign.com.au/Files/firmware/MPSiK%20V2.6%20rfd900p.ihx", firmwarefile); } - return Download.getFilefromNet("https://files.rfdesign.com.au/Files/firmware/RFDSiK%20V1.9%20rfd900p.ihx", firmwarefile); + return Download.getFilefromNet("http://files.rfdesign.com.au/Files/firmware/RFDSiK%20V1.9%20rfd900p.ihx", firmwarefile); } if (device == Uploader.Board.DEVICE_ID_RFD900X) { @@ -247,11 +391,22 @@ private bool getFirmware(Uploader.Board device, bool custom = false) /// Loads a local firmware file after prompting user for file. /// /// - private bool getFirmwareLocal(bool Bin) + private bool getFirmwareLocal(RFD.RFD900.RFD900 RFD900) { using (var openFileDialog1 = new OpenFileDialog()) { - openFileDialog1.Filter = Bin ? "Firmware|*.bin" : "Firmware|*.hex;*.ihx"; + string Filter = "Firmware|"; + string[] Exts = RFD900.FirmwareFileNameExtensions; + for (int n = 0; n < Exts.Length; n++) + { + if (n != 0) + { + Filter += ";"; + } + Filter += "*." + Exts[n]; + } + + openFileDialog1.Filter = Filter; openFileDialog1.RestoreDirectory = true; openFileDialog1.Multiselect = false; @@ -259,7 +414,8 @@ private bool getFirmwareLocal(bool Bin) { try { - File.Copy(openFileDialog1.FileName, firmwarefile, true); + firmwarefile = openFileDialog1.FileName; + //File.Copy(openFileDialog1.FileName, firmwarefile, true); } catch (Exception ex) { @@ -291,76 +447,6 @@ private void Sleep(int mstimeout, ICommsSerial comPort = null) } } - /// - /// Tries to upload firmware to the modem using xmodem mode (after prompting user for file) - /// - /// - /// - bool upload_xmodem(ICommsSerial comPort) - { - // try xmodem mode - // xmodem - short cts to ground - try - { - uploader_LogEvent("Trying XModem Mode"); - //comPort.BaudRate = 57600; - comPort.BaudRate = MainV2.comPort.BaseStream.BaudRate; - comPort.ReadTimeout = 1000; - - Thread.Sleep(2000); - var tempd = comPort.ReadExisting(); - Console.WriteLine(tempd); - comPort.Write("U"); - Thread.Sleep(1000); - var resp1 = Serial_ReadLine(comPort); // echo - var resp2 = Serial_ReadLine(comPort); // echo 2 - var tempd2 = comPort.ReadExisting(); // posibly bootloader info / use to sync - // identify - comPort.Write("i"); - // responce is rfd900.... - var resp3 = Serial_ReadLine(comPort); //echo - var resp4 = Serial_ReadLine(comPort); // newline - var resp5 = Serial_ReadLine(comPort); // bootloader info - uploader_LogEvent(resp5); - if (resp5.Contains("RFD900")) - { - // start upload - comPort.Write("u"); - var resp6 = Serial_ReadLine(comPort); // echo - var resp7 = Serial_ReadLine(comPort); // Ready - if (resp7.Contains("Ready")) - { - comPort.ReadTimeout = 3500; - // responce is C - var isC = comPort.ReadByte(); - var temp = comPort.ReadExisting(); - if (isC == 'C') - { - if (getFirmwareLocal(false)) - { - - XModem.LogEvent += uploader_LogEvent; - XModem.ProgressEvent += uploader_ProgressEvent; - // start file send - XModem.Upload(firmwarefile, - comPort); - XModem.LogEvent -= uploader_LogEvent; - XModem.ProgressEvent -= uploader_ProgressEvent; - return true; - } - return false; - } - } - } - } - catch (Exception ex2) - { - log.Error(ex2); - } - - return false; - } - private void BUT_upload_Click(object sender, EventArgs e) { ProgramFirmware(false); @@ -370,7 +456,7 @@ private void iHex_ProgressEvent(double completed) { try { - Progressbar.Value = (int)(completed * 100); + Progressbar.Value = (int) (completed*100); Application.DoEvents(); } catch @@ -420,7 +506,7 @@ private void uploader_ProgressEvent(double completed) { try { - Progressbar.Value = (int)Math.Min(completed * 100, 100); + Progressbar.Value = (int)Math.Min (completed*100,100); Application.DoEvents(); } catch @@ -435,305 +521,493 @@ string GetParamNumber(string Part1) return Part1.Substring(S + 1); } - private void BUT_savesettings_Click(object sender, EventArgs e) + int GetValueFromControl(Control control) { - //EndSession(); - var Session = GetSession(); - - if (Session == null) + if (control.GetType() == typeof(CheckBox)) { - return; + return ((CheckBox)control).Checked ? 1 : 0; } + else if (control is ComboBox) + { + string CBValue = GetCBValue((ComboBox)control); - EnableConfigControls(false); - EnableProgrammingControls(false); - lbl_status.Text = "Connecting"; + int Result; - if (Session.PutIntoATCommandMode() == RFD.RFD900.TSession.TMode.AT_COMMAND) + if (int.TryParse(CBValue, out Result)) + { + return Result; + } + else + { + return 0; + } + } + else { - // cleanup - doCommand(Session.Port, "AT&T", false, 1); + return 0; + } + } - Session.Port.DiscardInBuffer(); + class TBaseSetting + { + } - lbl_status.Text = "Doing Command"; + class TSetting : TBaseSetting + { + public T Value; + public TSetting(T Value) + { + this.Value = Value; + } + } - if (RTI.Text != "") + TBaseSetting GetSettingFromControl(Control control) + { + if (control.GetType() == typeof(CheckBox)) + { + return new TSetting(((CheckBox)control).Checked ? 1 : 0); + } + else if (control is TextBox) + { + return new TSetting(control.Text); + } + else if (control is ComboBox) + { + int x; + + if (int.TryParse(GetCBValue((ComboBox)control), out x)) { - // remote - var answer = doCommand(Session.Port, "RTI5", true); + return new TSetting(x); + } + } - var items = answer.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries); + return null; + } - foreach (var item in items) + /// + /// Returns whether x is different to y. + /// + /// + /// + /// + bool GetIsDifferent(RFD.RFD900.TBaseSetting x, TBaseSetting y) + { + if (y is TSetting) + { + return x is RFD.RFD900.TSetting && ((RFD.RFD900.TSetting)x).Value != ((TSetting)y).Value; + } + else if (y is TSetting) + { + return x is RFD.RFD900.TTextSetting && ((RFD.RFD900.TTextSetting)x).Text != ((TSetting)y).Value; + } + else + { + return false; + } + } + + /// + /// Make a clone of the given original setting. Update it's value with Value, and return it. + /// + /// The original setting. Must not be null. + /// The new value for the setting. Must not be null. + /// The new cloned setting with updated value. Never null. + RFD.RFD900.TBaseSetting UpdateSetting(RFD.RFD900.TBaseSetting Orig, TBaseSetting Value) + { + if (Value is TSetting) + { + RFD.RFD900.TSetting y = (RFD.RFD900.TSetting)Orig.Clone(); + y.Value = ((TSetting)Value).Value; + return y; + } + else + { + RFD.RFD900.TTextSetting y = (RFD.RFD900.TTextSetting)Orig.Clone(); + y.Text = ((TSetting)Value).Value; + return y; + } + } + + /// + /// Figure out which settings in the GUI have different values to the given original settings. + /// Return a list of the settings which have changed, and their new values. + /// + /// The original settings and their values. Must not be null. + /// The relevant groupbox. Must not be null. + /// true if the remote modem, false if the local modem. + /// + Dictionary GetUpdatedSettingsFromGroupBox( + Dictionary Orig, GroupBox GB, + bool Remote) + { + Dictionary Result = new Dictionary(); + + foreach (var kvp in Orig) + { + var control = FindControlInGroupBox(GB, (Remote ? "R" : "") + kvp.Key); + + if (control != null) + { + var S = GetSettingFromControl(control); + + if (S != null) { - //if (item.StartsWith("S")) + if (GetIsDifferent(kvp.Value, S)) { - int multipoint_fix = -1; - if (item.StartsWith("[")) + Result[kvp.Key] = UpdateSetting(kvp.Value, S); + } + } + } + } + + return Result; + } + + /// + /// Check that the settings in the given groupbox are valid. If invalid, shows a message box listing the issues. + /// + /// + /// Whether it's for the remote modem. + /// The settings read from the modem. Must not be null. + /// true if valid, false if invalid. + bool CheckSettingsValid(GroupBox GB, bool Remote, + Dictionary Orig) + { + var Changed = GetUpdatedSettingsFromGroupBox(Orig, GB, Remote); + + Dictionary NewSettings = new Dictionary(Orig); + RFDLib.Collections.Update(NewSettings, Changed); + + RFD.RFD900.TSettings Settings = new RFD.RFD900.TSettings(NewSettings); + + var Errors = Settings.CheckValid(); + + if (Errors.Length == 0) + { + return true; + } + else + { + string ErrorMsg = Remote ? "Remote" : "Local" + " settings invalid, operation aborted:"; + + foreach (var em in Errors) + { + ErrorMsg += "\n\t" + em; + } + + MsgBox.CustomMessageBox.Show(ErrorMsg); + + return false; + } + } + + /// + /// Save settings from GUI to a modem. + /// + /// The relevant groupbox. Must not be null. + /// true if remote modem, false if local. + /// The serial port to use. Must not be null. + /// The settings read from the modem. These are not modified by this function. Must not be null. + void SaveSettingsFromGroupBox(GroupBox GB, bool Remote, ICommsSerial Port, + Dictionary Orig) + { + var Changed = GetUpdatedSettingsFromGroupBox(Orig, GB, Remote); + string CommandPrefix = Remote ? "R" : "A"; + + foreach (var kvp in Changed) + { + if (!kvp.Key.Contains("FORMAT")) + { + var cmdanswer = doCommand(Port, + CommandPrefix + "T" + kvp.Value.Designator + "=" + kvp.Value.GetValueAsString(), + kvp.Value.Designator.Contains("&E")); + + if (cmdanswer.Contains("OK")) + { + if (kvp.Key.Contains("GPO1_1R_COUT") || + kvp.Key.Contains("GPO1_3SBUSOUT")) + { + if (kvp.Value is RFD.RFD900.TSetting && ((RFD.RFD900.TSetting)kvp.Value).Value == 1) { - multipoint_fix = item.IndexOf(']') + 1; + //Also need to set RTPO. + cmdanswer = doCommand(Port, + CommandPrefix + "TPO=1"); } - var mod_item = item; - if (multipoint_fix > 0 && item.Length > multipoint_fix) + else { - mod_item = item.Substring(multipoint_fix).Trim(); + cmdanswer = doCommand(Port, + CommandPrefix + "TPI=1"); } - var values = mod_item.Split(':', '='); + } + else if (kvp.Key.Contains("GPI1_1R_CIN") || + kvp.Key.Contains("GPO1_3SBUSIN")) + { + //Also need to set RTPI. + cmdanswer = doCommand(Port, + CommandPrefix + "TPI=1"); + } + if (!cmdanswer.Contains("OK")) + { + MsgBox.CustomMessageBox.Show("Set Command error"); + } - if (values.Length == 3) - { - values[1] = values[1].Replace("/", "_"); - - var controls = groupBoxRemote.Controls.Find("R" + values[1].Trim(), true); - - if (controls.Length > 0) - { - if (controls[0].GetType() == typeof(CheckBox)) - { - var value = ((CheckBox)controls[0]).Checked ? "1" : "0"; - - if (value != values[2].Trim()) - { - var cmdanswer = doCommand(Session.Port, - "RTS" + values[0].Trim().TrimStart('S') + "=" + value); - - if (cmdanswer.Contains("OK")) - { - } - else - { - if (values[1] == "ENCRYPTION_LEVEL") - { - // set this on the local radio as well. - doCommand(Session.Port, "ATS" + values[0].Trim().TrimStart('S') + "=" + value); - // both radios should now be using the default key - } - else - { - MsgBox.CustomMessageBox.Show("Set Command error"); - } - } - } - } - else if (!controls[0].Name.Contains("FORMAT") && controls[0] is TextBox) - { - if (controls[0].Text != values[2].Trim()) - { - var cmdanswer = doCommand(Session.Port, - "RTS" + values[0].Trim().TrimStart('S') + "=" + controls[0].Text); - - if (cmdanswer.Contains("OK")) - { - } - else - { - MsgBox.CustomMessageBox.Show("Set Command error"); - } - } - } - else if (controls[0].Name.Contains("MAVLINK")) // - { - if (((ComboBox)controls[0]).SelectedValue.ToString() != values[2].Trim()) - { - var cmdanswer = doCommand(Session.Port, - "RTS" + values[0].Trim().TrimStart('S') + "=" + ((ComboBox) controls[0]).SelectedValue); - - if (cmdanswer.Contains("OK")) - { - } - else - { - MsgBox.CustomMessageBox.Show("Set Command error"); - } - } - } - else if (controls[0] is ComboBox) - { - string CBValue = GetCBValue((ComboBox)controls[0]); - if (CBValue != values[2].Trim()) - { - var cmdanswer = doCommand(Session.Port, - "RTS" + values[0].Trim().TrimStart('S') + "=" + CBValue); - - if (cmdanswer.Contains("OK")) - { - } - else - { - MsgBox.CustomMessageBox.Show("Set Command error"); - } - } - } - } - } + } + else + { + if (Remote && (kvp.Key == "ENCRYPTION_LEVEL")) + { + // set this on the local radio as well. + doCommand(Port, "AT" + kvp.Value.Designator + "=" + kvp.Value.GetValueAsString()); + // both radios should now be using the default key + } + else + { + MsgBox.CustomMessageBox.Show("Set Command error"); } } + } + } + } - Sleep(100); + /// + /// Returns whether encryption is enabled, as per the given combo box. + /// + /// The combo box which is used to set encryption level. Must not be null. + /// true if enabled, otherwise false. + bool GetIsEncryptionEnabled(ComboBox CB) + { + return GetEncryptionLevelValue(CB) != 0; + } + + /// + /// Get the max encryption key length in hex numerals. + /// + /// The encryption level setting combo box. Must not be null. + /// The max encryption key length, in hex numerals. + int GetEncryptionMaxKeyLength(ComboBox CB) + { + if (CB.Text.Contains("b")) + { + var Parts = CB.Text.Split('b'); + if (Parts.Length >= 1) + { + int QTYBits; + + if (int.TryParse(Parts[0], out QTYBits)) + { + return QTYBits / 4; + } } - Session.Port.DiscardInBuffer(); + return 0; + } + else + { + if (CB.Text == "0") + { + return 0; + } + else { - //local - string answer = ""; - for (int n = 0; n < 5; n++) + return 32; + } + } + } + + /// + /// Get the encryption level setting number value. + /// + /// The combo box to get number value for. Must not be null. + /// The level setting number value. + int GetEncryptionLevelValue(ComboBox CB) + { + if (CB.Tag != null) + { + RFD.RFD900.TSetting Setting = (RFD.RFD900.TSetting)CB.Tag; + + if (Setting.Options != null) + { + foreach (var O in Setting.Options) { - answer = doCommand(Session.Port, "ATI5", true); - if (answer.Length != 0) + if (O.OptionName == CB.Text) { - break; + return O.Value; } } + } + } + + int Result; + if (int.TryParse(CB.Text, out Result)) + { + return Result; + } + return 0; + } + + private void BUT_savesettings_Click(object sender, EventArgs e) + { + if ( + ( + (RTI.Text == "") || + CheckSettingsValid + ( + groupBoxRemote, true, + RFDLib.Collections.Translate(_RemoteSettings.Settings, (x) => (RFD.RFD900.TBaseSetting)x) + ) + ) + && + CheckSettingsValid + ( + groupBoxLocal, false, + RFDLib.Collections.Translate(_LocalSettings.Settings, (x) => (RFD.RFD900.TBaseSetting)x) + ) + ) + { + + //EndSession(); + var Session = GetSession(); + + if (Session == null) + { + return; + } + + List EnabledControls = new List(); + foreach (Control C in groupBoxLocal.Controls) + { + if (C.Enabled) + { + EnabledControls.Add(C); + } + } + foreach (Control C in groupBoxRemote.Controls) + { + if (C.Enabled) + { + EnabledControls.Add(C); + } + } + + EnableConfigControls(false, false); + EnableProgrammingControls(false); + lbl_status.Text = "Connecting"; + + if (Session.PutIntoATCommandMode() == RFD.RFD900.TSession.TMode.AT_COMMAND) + { + // cleanup + doCommand(Session.Port, "AT&T", false, 1); + + Session.Port.DiscardInBuffer(); + + lbl_status.Text = "Doing Command"; - var items = answer.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries); - foreach (var item in items) + if (RTI.Text != "") { - //if (item.StartsWith("S")) - { - var values = item.Split(':', '='); + // remote + var answer = doCommand(Session.Port, "RTI5", true); - if (values.Length == 3) - { - values[1] = values[1].Replace("/", "_"); - - var controls = groupBoxLocal.Controls.Find(values[1].Trim(), true); - - if (controls.Length > 0) - { - if (controls[0].GetType() == typeof(CheckBox)) - { - var value = ((CheckBox)controls[0]).Checked ? "1" : "0"; - - if (value != values[2].Trim()) - { - var cmdanswer = doCommand(Session.Port, - "ATS" + GetParamNumber(values[0]) + "=" + value); - - if (cmdanswer.Contains("OK")) - { - } - else - { - MsgBox.CustomMessageBox.Show("Set Command error"); - } - } - } - else if (controls[0] is TextBox) - { - } - else if (controls[0].Name.Contains("MAVLINK")) // - { - if (((ComboBox)controls[0]).SelectedValue.ToString() != values[2].Trim()) - { - var cmdanswer = doCommand(Session.Port, - "ATS" + GetParamNumber(values[0]) + "=" + ((ComboBox) controls[0]).SelectedValue); - - if (cmdanswer.Contains("OK")) - { - } - else - { - MsgBox.CustomMessageBox.Show("Set Command error"); - } - } - } - else if (controls[0] is ComboBox) - { - string CBValue = GetCBValue((ComboBox)controls[0]); - if (CBValue != values[2].Trim()) - { - var cmdanswer = doCommand(Session.Port, - "ATS" + GetParamNumber(values[0]) + "=" + CBValue); - - if (cmdanswer.Contains("OK")) - { - } - else - { - MsgBox.CustomMessageBox.Show("Set Command error"); - } - } - } - } - } - } + SaveSettingsFromGroupBox(groupBoxRemote, true, Session.Port, + RFDLib.Collections.Translate(_RemoteSettings.Settings, (x) => (RFD.RFD900.TBaseSetting)x)); + + Sleep(100); } - // set encryption keys at the same time, so if we are enabled we dont lose comms. - // we have set encryption to on for both radios, they will be using the default key atm - if (RENCRYPTION_LEVEL.Checked) + Session.Port.DiscardInBuffer(); { - if (System.Text.RegularExpressions.Regex.IsMatch(txt_Raeskey.Text, @"\A\b[0-9a-fA-F]+\b\Z") - && (txt_Raeskey.Text.Length <= 32)) + //local + string answer = ""; + for (int n = 0; n < 5; n++) { - doCommand(Session.Port, "RT&E=" + txt_Raeskey.Text.PadRight(32, '0'), true); + answer = doCommand(Session.Port, "ATI5", true); + if (answer.Length != 0) + { + break; + } } - else + + SaveSettingsFromGroupBox(groupBoxLocal, false, Session.Port, + RFDLib.Collections.Translate(_LocalSettings.Settings, (x) => (RFD.RFD900.TBaseSetting)x)); + + // set encryption keys at the same time, so if we are enabled we dont lose comms. + // we have set encryption to on for both radios, they will be using the default key atm + if (GetIsEncryptionEnabled(RENCRYPTION_LEVEL)) { - //Complain that encryption key invalid. - lbl_status.Text = "Fail"; - MsgBox.CustomMessageBox.Show("Encryption key not valid hex number <= 32 hex numerals"); + int MaxKeyLength = GetEncryptionMaxKeyLength(RENCRYPTION_LEVEL); + RAESKEY.Text = RAESKEY.Text.Trim(); + + if (System.Text.RegularExpressions.Regex.IsMatch(RAESKEY.Text, @"\A\b[0-9a-fA-F]+\b\Z") + && (RAESKEY.Text.Length <= MaxKeyLength)) + { + doCommand(Session.Port, "RT&E=" + RAESKEY.Text.PadRight(MaxKeyLength, '0'), true); + } + else + { + //Complain that encryption key invalid. + lbl_status.Text = "Fail"; + MsgBox.CustomMessageBox.Show("Encryption key not valid hex number <= " + MaxKeyLength.ToString() + " hex numerals"); + } } - } - if (ENCRYPTION_LEVEL.Checked) - { - if (System.Text.RegularExpressions.Regex.IsMatch(txt_aeskey.Text, @"\A\b[0-9a-fA-F]+\b\Z") - && (txt_aeskey.Text.Length <= 32)) + if (GetIsEncryptionEnabled(ENCRYPTION_LEVEL)) { - doCommand(Session.Port, "AT&E=" + txt_aeskey.Text.PadRight(32, '0'), true); + int MaxKeyLength = GetEncryptionMaxKeyLength(ENCRYPTION_LEVEL); + AESKEY.Text = AESKEY.Text.Trim(); + + if (System.Text.RegularExpressions.Regex.IsMatch(AESKEY.Text, @"\A\b[0-9a-fA-F]+\b\Z") + && (AESKEY.Text.Length <= MaxKeyLength)) + { + doCommand(Session.Port, "AT&E=" + AESKEY.Text.PadRight(MaxKeyLength, '0'), true); + } + else + { + //Complain that encryption key invalid. + lbl_status.Text = "Fail"; + MsgBox.CustomMessageBox.Show("Encryption key not valid hex number <= " + MaxKeyLength.ToString() + " hex numerals"); + } } - else + + + if (RTI.Text != "") { - //Complain that encryption key invalid. - lbl_status.Text = "Fail"; - MsgBox.CustomMessageBox.Show("Encryption key not valid hex number <= 32 hex numerals"); - } - } + // write it + doCommand(Session.Port, "RT&W"); + // return to normal mode + doCommand(Session.Port, "RTZ"); + } - if (RTI.Text != "") - { // write it - doCommand(Session.Port, "RT&W"); + var cmdwriteanswer = doCommand(Session.Port, "AT&W"); + if (!cmdwriteanswer.Contains("OK")) + { + MsgBox.CustomMessageBox.Show("Failed to save parameters"); + } // return to normal mode - doCommand(Session.Port, "RTZ"); - } - - // write it - var cmdwriteanswer = doCommand(Session.Port, "AT&W"); - if (!cmdwriteanswer.Contains("OK")) - { - MsgBox.CustomMessageBox.Show("Failed to save parameters"); + doCommand(Session.Port, "ATZ"); } + lbl_status.Text = "Done"; + EnableConfigControls(true, true); + } + else + { // return to normal mode doCommand(Session.Port, "ATZ"); - } - - lbl_status.Text = "Done"; - } - else - { - // return to normal mode - doCommand(Session.Port, "ATZ"); - lbl_status.Text = "Fail"; - MsgBox.CustomMessageBox.Show("Failed to enter command mode"); - } + lbl_status.Text = "Fail"; + MsgBox.CustomMessageBox.Show("Failed to enter command mode"); + EnableConfigControls(true, false); + } - //Need to do this because modem rebooted. - Session.PutIntoATCommandModeAssumingInTransparentMode(); + //Need to do this because modem rebooted. + Session.PutIntoATCommandModeAssumingInTransparentMode(); - EnableConfigControls(true); - EnableProgrammingControls(true); + EnableProgrammingControls(true); - if (GetCommsSerialAlt == null) - { - EndSessionClosePort(); + UpdateSetPPMFailSafeButtons(); } } @@ -798,23 +1072,29 @@ public static IEnumerable Range(int start, int step, int end) } } - - private bool SetupCBWithSetting(ComboBox CB, Dictionary Settings, - string Value, bool Remote) + private void SetupCBWithDefaultEncryptionOptions(ComboBox CB) { - string SettingName; + CB.Tag = null; + CB.DataSource = Range(0, 1, 1); + CB.Text = "0"; + } - if (Remote) - { - SettingName = CB.Name.Substring(1); - } - else - { - SettingName = CB.Name; - } - if (Settings.ContainsKey(SettingName)) + /// + /// Set the given combo box to use the corresponding setting in the given dictionary of + /// settings. If no corresponding setting exists in that dictionary, just set it to the + /// given value. + /// + /// The combo box. Must not be null. + /// The settings. Must not be null. + /// The string value representation to set it to. Must not be null. + /// true if it is a remote modem setting, false if local. + /// + private bool SetupCBWithSetting(ComboBox CB, Dictionary Settings, + string Value, bool Remote, string SettingName) + { + if (Settings.ContainsKey(SettingName) && Settings[SettingName] is RFD.RFD900.TSetting) { - var Setting = Settings[SettingName]; + var Setting = (RFD.RFD900.TSetting)Settings[SettingName]; if (Setting.Options != null) { //Use options. @@ -826,7 +1106,7 @@ private bool SetupCBWithSetting(ComboBox CB, Dictionary dict; + if (Simple) + { + dict = Enum.GetValues(typeof(mavlink_option_simple)) + .Cast() + .ToDictionary(t => (int)t, t => t.ToString()); + } + else + { + dict = Enum.GetValues(typeof(mavlink_option)) + .Cast() + .ToDictionary(t => (int)t, t => t.ToString()); + } + + CB.DataSource = dict.ToArray(); + } + + /// + /// Given an array of lines returned from ATI5 command from a modem, + /// remove the "[n]" from the start of the lines. The "[n]" is returned + /// from a modem at the start of the lines if it is running multipoint firmware. + /// + /// The raw lines returned from the modem. Must not be null. + /// The character index into the string immediately after + /// the initial "[n]", or -1 if not multipoint. + /// The modified lines. Never null. + string[] ModifyReturnedStringsForMultipoint(string[] items, int multipoint_fix) + { + string[] Result = new string[items.Length]; + + for (int n = 0; n < items.Length; n++) + { + Result[n] = items[n]; + if (multipoint_fix > 0 && items[n].Length > multipoint_fix) + { + Result[n] = items[n].Substring(multipoint_fix).Trim(); + } + } + + return Result; + } + + Control FindControlInGroupBox(GroupBox GB, string Name) + { + Control Result = _DynamicLabelEditorPairRegister.FindAndSetUpEditorWithSettingName(Name); + if (Result == null) + { + var Array = GB.Controls.Find(Name, true); + + if (Array.Length == 0) + { + return null; + } + else + { + return Array[0]; + } + } + else + { + return Result; + } + } + + /// + /// Returns whether it can be determined that the setting (from the given Settings) with the given + /// SettingName only has one value/option available (i.e. setting can't be changed). + /// + /// The dictionary of settings for the modem. Must not be null. + /// The setting name. Must not be null. + /// Returns true if it can be determined that the setting can only have one value, otherwise false. + bool GetDoesCheckboxHaveOnlyOneOption(Dictionary Settings, string SettingName) + { + if (Settings.ContainsKey(SettingName)) + { + var BaseSetting = Settings[SettingName]; + + if (BaseSetting is RFD.RFD900.TSetting) + { + RFD.RFD900.TSetting Setting = (RFD.RFD900.TSetting)BaseSetting; + + if (Setting.Options != null) + { + if (Setting.Options.Length == 1) + { + return true; + } + } + if (Setting.Range != null) + { + if (Setting.Range.GetOptions().Length == 1) + { + return true; + } + } + } + } + + return false; + } + + /// + /// Configure and return a spare editor if one is available. + /// + /// The name of the setting to get the editor for. Must not be null. + /// The setting to get the editor for. Must not be null. + /// Whether it is the remote set of settings. + /// A control to use, or null if none available. + Control GetSpareEditor(string ThisSetting, RFD.RFD900.TSetting Setting, bool Remote) + { + RFDLib.GUI.Settings.TLabelEditorPairRegister Reg = Remote ? _RemoteLabelEditorPairs : _LocalLabelEditorPairs; + Control Result = null; + string Description = Remote ? ThisSetting.Substring(1) : ThisSetting; + + if (_KnownNameDescriptions.ContainsKey(Description)) + { + Description = _KnownNameDescriptions[Description]; + } + + if (Setting.GetIsFlag()) + { + Result = Reg.GetSpareCheckbox(ThisSetting, Description); + } + + if (Result == null) + { + Result = Reg.GetSpareComboBox(ThisSetting, Description); + } + + return Result; + } + + /// + /// Update the given setting control with the given setting value + /// + /// The control to update. Must not be null. + /// All of the settings read from the modem. Must not be null. + /// The name of the setting the control is for. Must not be null. + /// The setting value as text. Must not be null. + /// true if remote modem, false if local modem. + /// true if failed, false if successful. + bool UpdateControlWithValue(Control control, + Dictionary Settings, + string SettingName, + string Value, + bool Remote) + { + bool SomeSettingsInvalid = false; + control.Parent.Enabled = true; + control.Enabled = true; + + if (control is CheckBox) + { + ((CheckBox)control).Checked = Value == "1"; + //If the setting can only have one option/value... + if (GetDoesCheckboxHaveOnlyOneOption(Settings, SettingName)) + { + //Disable the control + control.Enabled = false; + } + } + else if (control is TextBox) + { + ((TextBox)control).Text = Value; + } + else if (Settings.ContainsKey(SettingName) && Settings[SettingName] is RFD.RFD900.TSetting) + { + if (control.Name.Contains("MAVLINK") && + !(Settings.ContainsKey("MAVLINK") && + (Settings["MAVLINK"] is RFD.RFD900.TSetting && + ((RFD.RFD900.TSetting)Settings["MAVLINK"]).Options != null))) // + { + var ans = Enum.Parse(typeof(mavlink_option), Value); + ((ComboBox)control).Text = ans.ToString(); + } + else if (control is ComboBox) + { + if (!SetupCBWithSetting((ComboBox)control, Settings, + Value, Remote, SettingName)) + { + ((ComboBox)control).Text = Value; + if (((ComboBox)control).Text != Value) + { + SomeSettingsInvalid = true; + } + } + } + } + + return SomeSettingsInvalid; + } + + /// + /// Update the settings controls in the given groupbox with the given set of settings. + /// + /// The group box. Must not be null. + /// true if it is for the remote modem, false if for the local modem. + /// The settings. Must not be null. + void UpdateControlsWithValues(GroupBox GB, bool Remote, Dictionary Settings) + { + foreach (var kvp in Settings) + { + string EditorName = (Remote ? "R" : "") + kvp.Key; + + var control = FindControlInGroupBox(GB, EditorName); + + if (control != null) + { + UpdateControlWithValue(control, Settings, EditorName, kvp.Value.GetValueAsString(), Remote); + } + } + } + + /// + /// Given a groupbox containing a set of controls, load them with the given values and settings. + /// + /// The groupbox. Must not be null. + /// true if it is the remote groupbox, false if it is the local groupbox. + /// The lines returned by ATI5 command. Must not be null. + /// The settings parsed from the modem. Must not be null. + /// true if at least one setting had an invalid value, otherwise false. + bool SetUpControlsWithValues(GroupBox GB, bool Remote, string[] items, Dictionary Settings) + { + /*RFD900Tools.GUI.frmCfgTest frm = new RFD900Tools.GUI.frmCfgTest(); + RFD900Tools.GUI.TConfig Cfg = new RFD900Tools.GUI.TConfig(frm.GetControl()); + Cfg.Update(Settings, items); + frm.Show();*/ + + _LocalLabelEditorPairs.SetUp(new List(Settings.Keys)); + _RemoteLabelEditorPairs.SetUp(new List( + RFDLib.Array.CherryPickArray(Settings.Keys.ToArray(), + (n) => "R" + n))); + + bool SomeSettingsInvalid = false; + + foreach (var item in items) + { + var values = item.Split(new char[] { ':', '=' }, StringSplitOptions.RemoveEmptyEntries); + + if (item.Contains("ANT_MODE")) + { + System.Diagnostics.Debug.WriteLine("Ant mode"); + } + + if (values.Length == 3) + { + string SettingName = values[1].Trim(); + string EditorName = values[1].Replace("/", "_").Trim(); + + var control = FindControlInGroupBox(GB, (Remote ? "R" : "") + EditorName); + + if (control == null) + { + if (Settings.ContainsKey(SettingName) && Settings[SettingName] is RFD.RFD900.TSetting) + { + var Setting = (RFD.RFD900.TSetting)Settings[SettingName]; + if (Setting.Range != null || Setting.Options != null) + { + control = GetSpareEditor((Remote ? "R" : "") + EditorName, Setting, Remote); + } + } + } + + if (control != null) + { + GB.Enabled = true; + SomeSettingsInvalid |= UpdateControlWithValue(control, + RFDLib.Collections.Translate(Settings, (x) => (RFD.RFD900.TBaseSetting)x), + SettingName, values[2].Trim(), Remote); + } + } + else + { + log.Info("Odd config line :" + item); + } + } + + return SomeSettingsInvalid; } - void SetupComboForMavlink(ComboBox CB, bool Simple) + /// + /// Get the country code from the modem as a string, or "--" if unknown or not locked to country. + /// + /// The session. Must not be null. + /// The function to get the country code, given the modem object. Must not be null. + /// The country code string. + string GetCountryCodeFromSession(RFD.RFD900.TSession Session, + Func GetCC) { - Dictionary dict; - if (Simple) + RFD.RFD900.RFD900xux.TCountry CC; + var Mdm = Session.GetModemObject(); + + if (Mdm == null || !(Mdm is RFD.RFD900.RFD900xuxRevN) || + !RFD.RFD900.RFD900xux.GetIsCountryLocked(CC = GetCC((RFD.RFD900.RFD900xuxRevN)Mdm))) { - dict = Enum.GetValues(typeof(mavlink_option_simple)) - .Cast() - .ToDictionary(t => (int)t, t => t.ToString()); + return "--"; } else { - dict = Enum.GetValues(typeof(mavlink_option)) - .Cast() - .ToDictionary(t => (int)t, t => t.ToString()); + return CC.ToString(); } - - CB.DataSource = dict.ToArray(); } /// @@ -888,6 +1466,8 @@ void SetupComboForMavlink(ComboBox CB, bool Simple) /// private void BUT_getcurrent_Click(object sender, EventArgs e) { + //System.Diagnostics.Stopwatch SW = new System.Diagnostics.Stopwatch(); + //SW.Start(); //EndSession(); var Session = GetSession(); @@ -896,38 +1476,62 @@ private void BUT_getcurrent_Click(object sender, EventArgs e) return; } + _AlreadyInEncCheckChangedEvtHdlr = true; + ResetAllControls(groupBoxLocal); ResetAllControls(groupBoxRemote); + SetupCBWithDefaultEncryptionOptions(ENCRYPTION_LEVEL); + SetupCBWithDefaultEncryptionOptions(RENCRYPTION_LEVEL); - EnableConfigControls(false); + EnableConfigControls(false, false); EnableProgrammingControls(false); lbl_status.Text = "Connecting"; + //System.Diagnostics.Debug.WriteLine(SW.ElapsedMilliseconds.ToString() + ": Putting into AT CMD mode"); + try { if (Session.PutIntoATCommandMode() == RFD.RFD900.TSession.TMode.AT_COMMAND) { + //System.Diagnostics.Debug.WriteLine(SW.ElapsedMilliseconds.ToString() + ": Done putting into AT CMD mode"); + bool SomeSettingsInvalid = false; // cleanup doCommand(Session.Port, "AT&T", false, 1); + /*Session.ATCClient.Timeout = 100; + Session.ATCClient.DoCommand("AT&T"); + Session.ATCClient.Timeout = 1000;*/ + //System.Diagnostics.Debug.WriteLine(SW.ElapsedMilliseconds.ToString() + ": Done AT&T cmd"); Session.Port.DiscardInBuffer(); lbl_status.Text = "Doing Command ATI & RTI"; //Set the text box to show the radio version - int multipoint_fix = -1; - var ati_str = doCommand(Session.Port, "ATI").Trim(); + int multipoint_fix = -1; //If this radio has multipoint firmware, the index within returned strings to use for returned values, otherwise -1. + var ati_str = doCommand(Session.Port, "ATI").Trim(); //Session.ATCClient.DoQuery("ATI", true);// doCommand(Session.Port, "ATI").Trim(); + //System.Diagnostics.Debug.WriteLine(SW.ElapsedMilliseconds.ToString() + ": Done ATI cmd"); + if (ati_str.StartsWith("[")) { multipoint_fix = ati_str.IndexOf(']') + 1; } ATI.Text = ati_str; + string LocalFWVer = RFD.RFD900.RFD900.ATIResponseToFWVersion(ati_str); + NumberStyles style = NumberStyles.Any; //Get the board frequency. - var freqstring = doCommand(Session.Port, "ATI3").Trim(); + var freqstring = doCommand(Session.Port, "ATI3").Trim(); //Session.ATCClient.DoQuery("ATI3", true);// doCommand(Session.Port, "ATI3").Trim(); + //System.Diagnostics.Debug.WriteLine(SW.ElapsedMilliseconds.ToString() + ": Done ATI3 cmd"); + + //Some multipoint firmware versions don't reply to ATI command with [n] at start of reply, but they do for ATI3 command, so check for [n] again... + if (multipoint_fix < 0 && freqstring.StartsWith("[")) + { + multipoint_fix = freqstring.IndexOf(']') + 1; + } + if (multipoint_fix > 0) { freqstring = freqstring.Substring(multipoint_fix).Trim(); @@ -938,15 +1542,16 @@ private void BUT_getcurrent_Click(object sender, EventArgs e) var freq = (Uploader.Frequency) - Enum.Parse(typeof(Uploader.Frequency), - int.Parse(freqstring.ToLower().Replace("x", ""), style, CultureInfo.InvariantCulture) - .ToString()); + Enum.Parse(typeof (Uploader.Frequency), + int.Parse(freqstring.ToLower().Replace("x", ""), style).ToString()); ATI3.Text = freq.ToString(); style = NumberStyles.Any; - var boardstring = doCommand(Session.Port, "ATI2").Trim(); + var boardstring = doCommand(Session.Port, "ATI2").Trim(); //Session.ATCClient.DoQuery("ATI2", true);// doCommand(Session.Port, "ATI2").Trim(); + //System.Diagnostics.Debug.WriteLine(SW.ElapsedMilliseconds.ToString() + ": Done ATI2 cmd"); + if (multipoint_fix > 0) { boardstring = boardstring.Substring(multipoint_fix).Trim(); @@ -957,8 +1562,11 @@ private void BUT_getcurrent_Click(object sender, EventArgs e) Session.Board = (Uploader.Board) - Enum.Parse(typeof(Uploader.Board), - int.Parse(boardstring.ToLower().Replace("x", ""), style, CultureInfo.InvariantCulture).ToString()); + Enum.Parse(typeof (Uploader.Board), + int.Parse(boardstring.ToLower().Replace("x", ""), style).ToString()); + + txtCountry.Text = GetCountryCodeFromSession(Session, + (m) => m.GetCountryCode()); ATI2.Text = Session.Board.ToString(); @@ -1003,19 +1611,19 @@ private void BUT_getcurrent_Click(object sender, EventArgs e) // 8 and 9 if (freq == Uploader.Frequency.FREQ_915) { - MIN_FREQ.DataSource = Range(895000, 1000, 935000); - RMIN_FREQ.DataSource = Range(895000, 1000, 935000); + MIN_FREQ.DataSource = Range(902000, 1000, 927000); + RMIN_FREQ.DataSource = Range(902000, 1000, 927000); - MAX_FREQ.DataSource = Range(895000, 1000, 935000); - RMAX_FREQ.DataSource = Range(895000, 1000, 935000); + MAX_FREQ.DataSource = Range(903000, 1000, 928000); + RMAX_FREQ.DataSource = Range(903000, 1000, 928000); } else if (freq == Uploader.Frequency.FREQ_433) { - MIN_FREQ.DataSource = Range(414000, 10, 460000); - RMIN_FREQ.DataSource = Range(414000, 10, 460000); + MIN_FREQ.DataSource = Range(414000, 50, 460000); + RMIN_FREQ.DataSource = Range(414000, 50, 460000); - MAX_FREQ.DataSource = Range(414000, 10, 460000); - RMAX_FREQ.DataSource = Range(414000, 10, 460000); + MAX_FREQ.DataSource = Range(414000, 50, 460000); + RMAX_FREQ.DataSource = Range(414000, 50, 460000); } else if (freq == Uploader.Frequency.FREQ_868) { @@ -1053,35 +1661,44 @@ private void BUT_getcurrent_Click(object sender, EventArgs e) if (multipoint_fix == -1) { - var AESKey = doCommand(Session.Port, "AT&E?").Trim(); + var AESKey = doCommand(Session.Port, "AT&E?").Trim(); //Session.ATCClient.DoQuery("AT&E?", true);// doCommand(Session.Port, "AT&E?").Trim(); if (AESKey.Contains("ERROR")) { - txt_aeskey.Text = ""; - txt_aeskey.Enabled = false; + AESKEY.Text = ""; + AESKEY.Enabled = false; } else { - txt_aeskey.Text = AESKey; - txt_aeskey.Enabled = true; + AESKEY.Text = AESKey; + AESKEY.Enabled = true; } SetupComboForMavlink(MAVLINK, false); } else { - txt_aeskey.Text = ""; - txt_aeskey.Enabled = false; + AESKEY.Text = ""; + AESKEY.Enabled = false; SetupComboForMavlink(MAVLINK, true); } - RSSI.Text = doCommand(Session.Port, "ATI7").Trim(); + RSSI.Text = doCommand(Session.Port, "ATI7").Trim(); //Session.ATCClient.DoQuery("ATI7", true);// doCommand(Session.Port, "ATI7").Trim(); + //System.Diagnostics.Debug.WriteLine(SW.ElapsedMilliseconds.ToString() + ": Done ATI7 cmd"); + lbl_status.Text = "Doing Command ATI5"; - var answer = doCommand(Session.Port, "ATI5", true); + var answer = doCommand(Session.Port, "ATI5", true); //Session.ATCClient.DoQueryWithMultiLineResponse("ATI5");// doCommand(Session.Port, "ATI5", true); + + bool Junk; + + //System.Diagnostics.Debug.WriteLine(SW.ElapsedMilliseconds.ToString() + ": Parsing local settings"); - var Settings = Session.GetSettings( - doCommand(Session.Port, "ATI5?", true), - Session.Board); + var Settings = Session.GetSettings(false, + Session.Board, answer, null, out Junk); + + _LocalSettings = new RFD.RFD900.TSettings( + RFDLib.Collections.Translate( + Settings, (x) => (RFD.RFD900.TBaseSetting)x)); DisableRFD900xControls(); @@ -1092,90 +1709,45 @@ private void BUT_getcurrent_Click(object sender, EventArgs e) kvp.Key.Enabled = kvp.Value; } + btnSaveToFile.Enabled = true; + btnLoadFromFile.Enabled = true; + + _LocalLabelEditorPairs.Reset(); + if (ATI.Text.Contains("ASYNC")) { - _LocalExtraParams.SetModel(Model.ASYNC); + _LocalExtraParams.SetModel(Model.ASYNC, Settings); } else { - if ((items.Length > 0) && items[0].StartsWith("[")) + if (ATI.Text.Contains("MP on") && (Session.Board == Uploader.Board.DEVICE_ID_RFD900X)) { //This is multipoint firmware. - _LocalExtraParams.SetModel(Model.MULTIPOINT); + _LocalExtraParams.SetModel(Model.MULTIPOINT_X, Settings); } - else if (ATI.Text.Contains("MP on") && (Session.Board == Uploader.Board.DEVICE_ID_RFD900X)) + else if ((items.Length > 0) && items[0].StartsWith("[")) { - _LocalExtraParams.SetModel(Model.MULTIPOINT_X); + _LocalExtraParams.SetModel(Model.MULTIPOINT, Settings); } else { //This is p2p firmware. - _LocalExtraParams.SetModel(Model.P2P); + _LocalExtraParams.SetModel(Model.P2P, Settings); } } + //System.Diagnostics.Debug.WriteLine(SW.ElapsedMilliseconds.ToString() + ": Done getting info out of local modem"); + //For each of the settings returned by the radio... - foreach (var item in items) - { - //if (item.StartsWith("S")) - { - var mod_item = item; - if (multipoint_fix > 0 && item.Length > multipoint_fix) - { - mod_item = item.Substring(multipoint_fix).Trim(); - } - var values = mod_item.Split(new char[] { ':', '=' }, StringSplitOptions.RemoveEmptyEntries); + SetUpControlsWithValues(groupBoxLocal, false, ModifyReturnedStringsForMultipoint(items, multipoint_fix), Settings); - if (values.Length == 3) - { - values[1] = values[1].Replace("/", "_"); - - var controls = groupBoxLocal.Controls.Find(values[1].Trim(), true); - - //If there's a control with the same name as the setting... - if (controls.Length > 0) - { - groupBoxLocal.Enabled = true; - controls[0].Parent.Enabled = true; - controls[0].Enabled = true; - - if (controls[0] is CheckBox) - { - ((CheckBox)controls[0]).Checked = values[2].Trim() == "1"; - } - else if (controls[0] is TextBox) - { - ((TextBox)controls[0]).Text = values[2].Trim(); - } - else if (controls[0].Name.Contains("MAVLINK")) // - { - var ans = Enum.Parse(typeof(mavlink_option), values[2].Trim()); - ((ComboBox)controls[0]).Text = ans.ToString(); - } - else if (controls[0] is ComboBox) - { - if (!SetupCBWithSetting((ComboBox)controls[0], Settings, - values[2].Trim(), false)) - { - ((ComboBox)controls[0]).Text = values[2].Trim(); - if (((ComboBox)controls[0]).Text != values[2].Trim()) - { - SomeSettingsInvalid = true; - - } - - } - - } - } - } - } - } + btnRandom.Enabled = GetIsEncryptionEnabled(ENCRYPTION_LEVEL); + //System.Diagnostics.Debug.WriteLine(SW.ElapsedMilliseconds.ToString() + ": Done setting up controls for local modem"); // remote foreach (Control ctl in groupBoxRemote.Controls) { - if ((ctl.Name != "RSSI")&& (!(ctl is Label))) + if ((ctl.Name != "RSSI")&& (!(ctl is Label)) && !(ctl is Button)) { ctl.ResetText(); } @@ -1183,7 +1755,20 @@ private void BUT_getcurrent_Click(object sender, EventArgs e) Session.Port.DiscardInBuffer(); - RTI.Text = doCommand(Session.Port, "RTI"); + string RTIText = doCommand(Session.Port, "RTI"); + + if ((RTIText.Length < 5) || RTIText.Substring(0, 5) == "ERROR") + { + RTI.Text = ""; + } + else + { + RTI.Text = RTIText; + } + + string RemoteFWVer = RFD.RFD900.RFD900.ATIResponseToFWVersion(RTI.Text); + + txtRCountry.Text = GetCountryCodeFromSession(Session, (m) => m.GetRemoteCountryCode()); if (RTI.Text != "") { @@ -1204,20 +1789,20 @@ private void BUT_getcurrent_Click(object sender, EventArgs e) var AESKey = doCommand(Session.Port, "RT&E?").Trim(); if (AESKey.Contains("ERROR")) { - txt_Raeskey.Text = ""; - txt_Raeskey.Enabled = false; + RAESKEY.Text = ""; + RAESKEY.Enabled = false; } else { - txt_Raeskey.Text = AESKey; - txt_Raeskey.Enabled = true; + RAESKEY.Text = AESKey; + RAESKEY.Enabled = true; } SetupComboForMavlink(RMAVLINK, false); } else { - txt_Raeskey.Text = ""; - txt_Raeskey.Enabled = false; + RAESKEY.Text = ""; + RAESKEY.Enabled = false; SetupComboForMavlink(RMAVLINK, true); } @@ -1225,92 +1810,53 @@ private void BUT_getcurrent_Click(object sender, EventArgs e) answer = doCommand(Session.Port, "RTI5", true); + bool UsedAltRanges; + + var RemoteSettings = Session.GetSettings(true, Session.Board, answer, (LocalFWVer == RemoteFWVer) ? Settings : null, out UsedAltRanges); + + _RemoteSettings = new RFD.RFD900.TSettings( + RFDLib.Collections.Translate( + RemoteSettings, (x) => (RFD.RFD900.TBaseSetting)x)); + + if ((RemoteFWVer != null) && (LocalFWVer != RemoteFWVer) && UsedAltRanges) + { + MsgBox.CustomMessageBox.Show("The ranges and options shown for the remote modem may not be accurate. To ensure accurate, use the same firmware version in both the local and remote modems"); + } + items = answer.Split('\n'); + _RemoteLabelEditorPairs.Reset(); + + btnRemoteSaveToFile.Enabled = true; + btnRemoteLoadFromFile.Enabled = true; if (RTI.Text.Contains("ASYNC")) { - _RemoteExtraParams.SetModel(Model.ASYNC); + _RemoteExtraParams.SetModel(Model.ASYNC, RemoteSettings); } else { - if ((items.Length > 0) && items[0].StartsWith("[")) + if (RTI.Text.Contains("MP on") && (Session.Board == Uploader.Board.DEVICE_ID_RFD900X)) { - //This is multipoint firmware. - _RemoteExtraParams.SetModel(Model.MULTIPOINT); + _RemoteExtraParams.SetModel(Model.MULTIPOINT_X, RemoteSettings); } - else if (RTI.Text.Contains("MP on") && (Session.Board == Uploader.Board.DEVICE_ID_RFD900X)) + else if ((items.Length > 0) && items[0].StartsWith("[")) { - _RemoteExtraParams.SetModel(Model.MULTIPOINT_X); + //This is multipoint firmware. + _RemoteExtraParams.SetModel(Model.MULTIPOINT, RemoteSettings); } else { //This is 2-point firmware. - _RemoteExtraParams.SetModel(Model.P2P); + _RemoteExtraParams.SetModel(Model.P2P, RemoteSettings); } } - foreach (var item in items) - { - //if (item.StartsWith("S")) - { - var values = item.Split(new char[] { ':', '=' }, StringSplitOptions.RemoveEmptyEntries); - - if (values.Length == 3) - { - values[1] = values[1].Replace("/", "_"); - - var controls = groupBoxRemote.Controls.Find("R" + values[1].Trim(), true); - - if (controls.Length == 0) - continue; - - - controls[0].Enabled = true; - - if (controls[0] is CheckBox) - { - ((CheckBox)controls[0]).Checked = values[2].Trim() == "1"; - } - else if (controls[0] is TextBox) - { - ((TextBox)controls[0]).Text = values[2].Trim(); - } - else if (controls[0].Name.Contains("MAVLINK")) // - { - var ans = Enum.Parse(typeof(mavlink_option), values[2].Trim()); - ((ComboBox)controls[0]).Text = ans.ToString(); - } - else if (controls[0] is ComboBox) - { - if (!SetupCBWithSetting((ComboBox)controls[0], Settings, - values[2].Trim(), true)) - { - ((ComboBox)controls[0]).Text = values[2].Trim(); - if (((ComboBox)controls[0]).Text != values[2].Trim()) - { - SomeSettingsInvalid = true; - } - } - } - } - else - { - /* - if (item.Contains("S15")) - { - answer = doCommand(comPort, "RTS15?"); - int rts15 = 0; - if (int.TryParse(answer, out rts15)) - { - RS15.Enabled = true; - RS15.Text = rts15.ToString(); - } - } - */ - log.Info("Odd config line :" + item); - } - } - } + //System.Diagnostics.Debug.WriteLine(SW.ElapsedMilliseconds.ToString() + ": Done getting info from remote modem"); + + SomeSettingsInvalid |= SetUpControlsWithValues(groupBoxRemote, true, items, RemoteSettings); + + //System.Diagnostics.Debug.WriteLine(SW.ElapsedMilliseconds.ToString() + ": Done setting up controls for remote modem"); + } // off hook @@ -1324,6 +1870,9 @@ private void BUT_getcurrent_Click(object sender, EventArgs e) { lbl_status.Text = "Done"; } + EnableConfigControls(true, true); + + } else { @@ -1331,7 +1880,8 @@ private void BUT_getcurrent_Click(object sender, EventArgs e) Session.PutIntoTransparentMode(); lbl_status.Text = "Fail"; - MsgBox.CustomMessageBox.Show("Failed to enter command mode"); + MsgBox.CustomMessageBox.Show("Failed to enter command mode. Try power-cycling modem."); + EnableConfigControls(true, false); } //BUT_Syncoptions.Enabled = true; @@ -1339,8 +1889,6 @@ private void BUT_getcurrent_Click(object sender, EventArgs e) //BUT_savesettings.Enabled = true; //BUT_SetPPMFailSafe.Enabled = true; - - EnableConfigControls(true); EnableProgrammingControls(true); } @@ -1349,11 +1897,15 @@ private void BUT_getcurrent_Click(object sender, EventArgs e) lbl_status.Text = "Error"; MsgBox.CustomMessageBox.Show("Error during read " + ex); } + _AlreadyInEncCheckChangedEvtHdlr = false; - if (GetCommsSerialAlt == null) - { - EndSessionClosePort(); - } + UpdateSetPPMFailSafeButtons(); + } + + void UpdateSetPPMFailSafeButtons() + { + BUT_SetPPMFailSafe.Enabled = GPO1_1R_COUT.Enabled && GPO1_1R_COUT.Checked; + BUT_SetPPMFailSafeRemote.Enabled = RGPO1_1R_COUT.Enabled && RGPO1_1R_COUT.Checked; } private string Serial_ReadLine(ICommsSerial comPort) @@ -1365,8 +1917,8 @@ private string Serial_ReadLine(ICommsSerial comPort) { if (comPort.BytesToRead > 0) { - var data = (byte)comPort.ReadByte(); - sb.Append((char)data); + var data = (byte) comPort.ReadByte(); + sb.Append((char) data); if (data == '\n') break; } @@ -1463,7 +2015,7 @@ public bool doConnect(ICommsSerial comPort) // setup a known enviroment comPort.Write("ATO\r\n"); - retry: + retry: // wait Sleep(1500, comPort); @@ -1531,8 +2083,8 @@ private void BUT_Syncoptions_Click(object sender, EventArgs e) RMAX_FREQ.Text = MAX_FREQ.Text; RNUM_CHANNELS.Text = NUM_CHANNELS.Text; RMAX_WINDOW.Text = MAX_WINDOW.Text; - RENCRYPTION_LEVEL.Checked = ENCRYPTION_LEVEL.Checked; - txt_Raeskey.Text = txt_aeskey.Text; + RENCRYPTION_LEVEL.SelectedIndex = ENCRYPTION_LEVEL.SelectedIndex; + RAESKEY.Text = AESKEY.Text; } private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) @@ -1608,11 +2160,7 @@ private void BUT_resettodefault_Click(object sender, EventArgs e) Session.PutIntoTransparentMode(); lbl_status.Text = "Fail"; - MsgBox.CustomMessageBox.Show("Failed to enter command mode"); - } - if (GetCommsSerialAlt == null) - { - EndSessionClosePort(); + MsgBox.CustomMessageBox.Show("Failed to enter command mode. Try power-cycling modem."); } } @@ -1623,14 +2171,26 @@ void UpdateStatus(string Status) Application.DoEvents(); } - void EnableConfigControls(bool Enable) + void UpdateStatusCallback(string Status, double Progress) + { + if (Status != null) + { + UpdateStatus(Status); + } + if (!double.IsNaN(Progress)) + { + ProgressEvtHdlr(Progress); + } + } + + void EnableConfigControls(bool Enable, bool SaveSettingsEnable) { - groupBoxLocal.Enabled = Enable; - groupBoxRemote.Enabled = Enable; + groupBoxLocal.Enabled = SaveSettingsEnable; + groupBoxRemote.Enabled = SaveSettingsEnable; BUT_Syncoptions.Enabled = Enable; BUT_SetPPMFailSafe.Enabled = Enable; BUT_getcurrent.Enabled = Enable; - BUT_savesettings.Enabled = Enable; + BUT_savesettings.Enabled = SaveSettingsEnable; BUT_resettodefault.Enabled = Enable; } public static void ResetAllControls(Control form) @@ -1688,7 +2248,7 @@ private void BUT_loadcustom_Click(object sender, EventArgs e) void ProgramFirmware(bool Custom) { EnableProgrammingControls(false); - EnableConfigControls(false); + EnableConfigControls(false, false); try { @@ -1700,23 +2260,13 @@ void ProgramFirmware(bool Custom) //Console.WriteLine("Mode is " + Mode.ToString()); //port.Close(); - RFD.RFD900.RFD900 RFD900 = null; - UpdateStatus("Putting in to bootloader mode"); - switch (Session.PutIntoBootloaderMode()) - { - case RFD.RFD900.TSession.TMode.BOOTLOADER: - RFD900 = RFD.RFD900.RFD900APU.GetObjectForModem(Session); - break; - case RFD.RFD900.TSession.TMode.BOOTLOADER_X: - RFD900 = RFD.RFD900.RFD900xux.GetObjectForModem(Session); - break; - default: - break; - } + RFD.RFD900.RFD900 RFD900 = _Session.GetModemObject(); if (RFD900 == null) { - UpdateStatus("Could not put in to bootloader mode"); + UpdateStatus("Unknown modem"); + MsgBox.CustomMessageBox.Show("Couldn't communicate with modem. Try power-cycling modem."); + EndSession(); } else { @@ -1728,10 +2278,10 @@ void ProgramFirmware(bool Custom) { UpdateStatus("Getting firmware from internet"); } - if (getFirmware(RFD900.Board, Custom)) + if (getFirmware(RFD900.Board, RFD900, Custom)) { UpdateStatus("Programming firmware into device"); - if (RFD900.ProgramFirmware(firmwarefile, ProgressEvtHdlr)) + if (RFD900.ProgramFirmware(firmwarefile, UpdateStatusCallback)) { UpdateStatus("Programmed firmware into device"); } @@ -1739,6 +2289,7 @@ void ProgramFirmware(bool Custom) { UpdateStatus("Programming failed. (Try again?)"); } + EndSession(); } else { @@ -1748,15 +2299,18 @@ void ProgramFirmware(bool Custom) } catch { - + try + { + UpdateStatus("Programming failed. (Try again?)"); + EndSession(); + } + catch + { + } } EnableProgrammingControls(true); - EnableConfigControls(true); + EnableConfigControls(true, false); //UploadFW(true); - if (GetCommsSerialAlt == null) - { - EndSessionClosePort(); - } } void ProgressEvtHdlr(double Completed) @@ -1799,7 +2353,7 @@ private void linkLabel_lowlatency_LinkClicked(object sender, LinkLabelLinkClicke RMAX_WINDOW.Text = 33.ToString(); } - private enum mavlink_option + public enum mavlink_option { RawData = 0, Mavlink = 1, @@ -1819,15 +2373,14 @@ private void txt_aeskey_TextChanged(object sender, EventArgs e) string item = txt.Text; if (!(Regex.IsMatch(item, "^[0-9a-fA-F]+$"))) { - if (item.Length != 0) + if(item.Length != 0) txt.Text = item.Remove(item.Length - 1, 1); txt.SelectionStart = txt.Text.Length; } } - private void BUT_SetPPMFailSafe_Click(object sender, EventArgs e) + private void SetPPMFailSafe(string SetCmd, string SaveCmd) { - lbl_status.Text = "Connecting"; RFD.RFD900.TSession Session = GetSession(); @@ -1839,18 +2392,21 @@ private void BUT_SetPPMFailSafe_Click(object sender, EventArgs e) if (Session.PutIntoATCommandMode() == RFD.RFD900.TSession.TMode.AT_COMMAND) { // cleanup - doCommand(Session.Port, "AT&T", false, 1); - - Session.Port.DiscardInBuffer(); + //Session.Port.DiscardInBuffer(); + //doCommand(Session.Port, "AT&T", false, 1); lbl_status.Text = "Doing Command"; - string Result = doCommand(Session.Port, "RT&R"); + Session.Port.DiscardInBuffer(); + bool Result = Session.ATCClient.DoCommand(SetCmd); + + Session.Port.DiscardInBuffer(); + Session.ATCClient.DoCommand(SaveCmd); // off hook //doCommand(Session.Port, "ATO"); - if (Result.Contains("OK")) + if (Result) { lbl_status.Text = "Done"; } @@ -1867,85 +2423,26 @@ private void BUT_SetPPMFailSafe_Click(object sender, EventArgs e) lbl_status.Text = "Fail"; MsgBox.CustomMessageBox.Show("Failed to enter command mode"); } - - if (GetCommsSerialAlt == null) - { - EndSessionClosePort(); - } } - void getTelemPortWithRadio(ref ICommsSerial comPort) - { - // try telem1 - - comPort = new MAVLinkSerialPort(MainV2.comPort, (int)MAVLink.SERIAL_CONTROL_DEV.TELEM1); - - comPort.ReadTimeout = 4000; - - comPort.Open(); - } - /// - /// returns null if failed - /// - /// - ICommsSerial GetComPort() + private void BUT_SetPPMFailSafe_Click(object sender, EventArgs e) { - if (GetCommsSerialAlt == null) - { - ICommsSerial comPort; - - try - { - if (MainV2.comPort.BaseStream.PortName.Contains("TCP")) - { - comPort = new TcpSerial(); - comPort.BaudRate = MainV2.comPort.BaseStream.BaudRate; - comPort.ReadTimeout = 4000; - comPort.Open(); - } - else - { - comPort = new SerialPort(); - if (MainV2.comPort.BaseStream.IsOpen) - { - getTelemPortWithRadio(ref comPort); - } - else - { - comPort.PortName = MainV2.comPortName; - comPort.BaudRate = MainV2.comPortBaud; - } - - comPort.ReadTimeout = 4000; - - comPort.Open(); - } - - return comPort; - } - catch - { - MsgBox.CustomMessageBox.Show("Invalid ComPort or in use"); - return null; - } - } - else - { - return GetCommsSerialAlt(); - } + SetPPMFailSafe("AT&R", "AT&W"); } RFD.RFD900.TSession GetSession() { + if (_Session == null) { try { - var p = GetComPort(); + var p = MissionPlanner.Radio.ComPort.GetComPortForSiKRadio(); + if (p != null) { - _Session = new RFD.RFD900.TSession(p); + _Session = new RFD.RFD900.TSession(p, MainV2.comPort.BaseStream.BaudRate); } } catch @@ -1964,29 +2461,16 @@ RFD.RFD900.TSession GetSession() return _Session; } - /// - /// Put the modem into transparent mode and close the comms port. - /// - private void EndSessionClosePort() + private void EndSession() { if (_Session != null) { - try - { - _Session.PutIntoTransparentMode(); - } catch{} _Session.Dispose(); - _Session.Port.Close(); _Session = null; } - } - - private void EndSession() - { - if (_Session != null) + if (DoDisconnectReconnect != null) { - _Session.Dispose(); - _Session = null; + DoDisconnectReconnect(); } } @@ -2012,8 +2496,11 @@ bool SetSetting(string Designator, int Value, bool Remote) /// ignored private void ENCRYPTION_LEVEL_CheckedChanged(object sender, EventArgs e) { - EncryptionCheckChangedEvtHdlr(ENCRYPTION_LEVEL, "ATI5", "AT&E?", txt_aeskey, false); - btnRandom.Enabled = ENCRYPTION_LEVEL.Checked; + if (ENCRYPTION_LEVEL.Enabled) + { + EncryptionCheckChangedEvtHdlr(ENCRYPTION_LEVEL, "ATI5", "AT&E?", AESKEY, false, "ATI5"); + } + btnRandom.Enabled = GetIsEncryptionEnabled(ENCRYPTION_LEVEL); } /// @@ -2023,19 +2510,38 @@ private void ENCRYPTION_LEVEL_CheckedChanged(object sender, EventArgs e) /// ignored private void RENCRYPTION_LEVEL_CheckedChanged(object sender, EventArgs e) { - EncryptionCheckChangedEvtHdlr(RENCRYPTION_LEVEL, "RTI5", "RT&E?", txt_Raeskey, true); + if (RENCRYPTION_LEVEL.Enabled) + { + EncryptionCheckChangedEvtHdlr(RENCRYPTION_LEVEL, "RTI5", "RT&E?", RAESKEY, true, "RTI5"); + } } bool _AlreadyInEncCheckChangedEvtHdlr = false; + string RemoveMultiPointLocalNodeID(string ATCReply) + { + ATCReply = ATCReply.Trim(); + + if ((ATCReply.Length > 1) && (ATCReply[0] == '[') && ATCReply.Contains(']')) + { + int StartIndex = ATCReply.IndexOf(']') + 1; + + return ATCReply.Substring(StartIndex); + } + else + { + return ATCReply; + } + } + /// /// Handles a change of an encryption level check box. /// /// The checkbox which was changed. Must not be null. /// The AT command to use to get the settings /// from the relevant modem. Must not be null. - void EncryptionCheckChangedEvtHdlr(CheckBox CB, string ATCommand, string EncKeyQuery, - TextBox EncKeyTextBox, bool Remote) + void EncryptionCheckChangedEvtHdlr(ComboBox CB, string ATCommand, string EncKeyQuery, + TextBox EncKeyTextBox, bool Remote, string ATI5Command) { if (_AlreadyInEncCheckChangedEvtHdlr) { @@ -2052,12 +2558,15 @@ void EncryptionCheckChangedEvtHdlr(CheckBox CB, string ATCommand, string EncKeyQ return; } Session.PutIntoATCommandMode(); - var answer = doCommand(Session.Port, ATCommand, true); - var Settings = Session.GetSettings(answer, Session.Board); + var ATI5answer = doCommand(Session.Port, ATI5Command, true); + + bool Junk; + + var Settings = Session.GetSettings(Remote, Session.Board, ATI5answer, null, out Junk); if (Settings.ContainsKey("ENCRYPTION_LEVEL")) { var Setting = Settings["ENCRYPTION_LEVEL"]; - if (!SetSetting(Setting.Designator, CB.Checked ? 1 : 0, Remote)) + if (!SetSetting(Setting.Designator, GetEncryptionLevelValue(CB), Remote)) { return; } @@ -2070,11 +2579,7 @@ void EncryptionCheckChangedEvtHdlr(CheckBox CB, string ATCommand, string EncKeyQ //Read AES key back out of modem and display it. //BUT_getcurrent_Click(this, null); //txt_aeskey.Text = doCommand(Session.Port, "AT&E?").Trim(); - EncKeyTextBox.Text = doCommand(Session.Port, EncKeyQuery).Trim(); - if (CB.Checked && EncKeyTextBox.Text.Length == 0) - { - //Console.WriteLine("Something wrong here"); - } + EncKeyTextBox.Text = RemoveMultiPointLocalNodeID(doCommand(Session.Port, EncKeyQuery).Trim()).Trim(); lbl_status.Text = "Done."; } finally @@ -2083,27 +2588,144 @@ void EncryptionCheckChangedEvtHdlr(CheckBox CB, string ATCommand, string EncKeyQ } } - string GetRandom32BitKey() + /// + /// Get a random key as a hex numeral string, with the given QTY of hex numerals. + /// + /// The QTY of hex numerals. + /// The key. Never null. + string GetRandomKey(int QTYHexDigits) { + string Result = ""; + System.Random R = new Random((int)(System.DateTime.Now.Ticks & 0xFFFFFFFF)); - return R.Next().ToString("X8"); - } - string GetRandomKey() - { - return GetRandom32BitKey() + GetRandom32BitKey() + GetRandom32BitKey() + GetRandom32BitKey(); + for (; QTYHexDigits > 0; QTYHexDigits--) + { + Result += (((UInt32)R.Next()) & 0xF).ToString("X1"); + } + + return Result; } private void btnRandom_Click(object sender, EventArgs e) { - //Key is 16 binary bytes (32 hex digits) - txt_aeskey.Text = GetRandomKey(); - txt_Raeskey.Text = txt_aeskey.Text; + AESKEY.Text = GetRandomKey(GetEncryptionMaxKeyLength(ENCRYPTION_LEVEL)); + RAESKEY.Text = AESKEY.Text; } private void btnCommsLog_Click(object sender, EventArgs e) { } + + private void BUT_SetPPMFailSafeRemote_Click(object sender, EventArgs e) + { + SetPPMFailSafe("RT&R", "RT&W"); + } + + /// + /// Save settings from GUI to file. + /// + /// The settings read from the modem. Must not be null. + /// The relevant GUI groupbox. + /// true if remote modem, false if local modem. + void SaveToFile(RFD.RFD900.TSettings S, GroupBox GB, + bool Remote) + { + //Get the settings which have changed in the GUI, and their values. + var Updated = GetUpdatedSettingsFromGroupBox( + RFDLib.Collections.Translate(S.Settings, (x) => (RFD.RFD900.TBaseSetting)x), + GB, Remote); + + //Include the settings which haven't changed. + foreach (var kvp in S.Settings) + { + if (!Updated.ContainsKey(kvp.Key)) + { + Updated[kvp.Key] = kvp.Value; + } + } + + //Save to file... + RFD.RFD900.TSettings ToSave = new RFD.RFD900.TSettings(Updated); + + if (dlgSave.ShowDialog() == DialogResult.OK) + { + if (ToSave.SaveToFile(dlgSave.FileName)) + { + System.Windows.Forms.MessageBox.Show("Saved settings to " + dlgSave.FileName + " OK"); + } + else + { + System.Windows.Forms.MessageBox.Show("Failed to save settings to " + dlgSave.FileName); + } + } + } + + private void btnSaveToFile_Click(object sender, EventArgs e) + { + SaveToFile(_LocalSettings, groupBoxLocal, false); + } + + /// + /// Load settings from file into the GUI. + /// + /// The settings loaded from the modem. These aren't modified by this function. Must not be null. + /// The relevant GUI groupbox. Must not be null. + /// true if for the remote modem, false if for the local modem. + private void LoadFromFile(RFD.RFD900.TSettings S, GroupBox GB, bool Remote) + { + if (dlgOpen.ShowDialog() == DialogResult.OK) + { + S = S.Clone(); + + var x = S.LoadFromFile(dlgOpen.FileName); + + if (x == null) + { + System.Windows.Forms.MessageBox.Show("Failed to load settings from " + dlgOpen.FileName); + } + else + { + UpdateControlsWithValues(GB, Remote, S.Settings); + + string Temp = "Loaded\n"; + + foreach (var kvp in x) + { + Temp += kvp.Value.Name + " = " + kvp.Value.Value.ToString() + "\n"; + } + + Temp += "from " + dlgOpen.FileName + " OK"; + + System.Windows.Forms.MessageBox.Show(Temp); + } + } + } + + + private void btnLoadFromFile_Click(object sender, EventArgs e) + { + LoadFromFile(_LocalSettings, groupBoxLocal, false); + } + + private void btnRemoteLoadFromFile_Click(object sender, EventArgs e) + { + LoadFromFile(_RemoteSettings, groupBoxRemote, true); + } + + private void btnRemoteSaveToFile_Click(object sender, EventArgs e) + { + SaveToFile(_RemoteSettings, groupBoxRemote, true); + } + + public string Header + { + get + { + return "Settings"; + } + } + } } \ No newline at end of file diff --git a/Radio/Sikradio.resx b/Radio/Sikradio.resx index bc9078141d..aad3baa0cf 100644 --- a/Radio/Sikradio.resx +++ b/Radio/Sikradio.resx @@ -123,7 +123,7 @@ - 12, 447 + 12, 561 1072, 36 @@ -179,7 +179,7 @@ which result in a valid packet CRC groupBoxLocal - 63 + 86 True @@ -216,7 +216,7 @@ red LED solid - in firmware update mode $this - 4 + 3 False @@ -225,7 +225,7 @@ red LED solid - in firmware update mode NoControl - 266, 217 + 266, 207 80, 20 @@ -246,7 +246,7 @@ red LED solid - in firmware update mode groupBoxRemote - 42 + 65 False @@ -345,7 +345,7 @@ red LED solid - in firmware update mode 30 - 266, 244 + 266, 232 80, 21 @@ -353,9 +353,6 @@ red LED solid - in firmware update mode 96 - - Node ID. Base node ID is 0. One node must be acting as a base for a multipoint environment to work. - RMAX_WINDOW @@ -366,7 +363,7 @@ red LED solid - in firmware update mode groupBoxRemote - 45 + 68 False @@ -399,7 +396,7 @@ red LED solid - in firmware update mode 435000 - 266, 108 + 266, 107 80, 21 @@ -420,7 +417,7 @@ red LED solid - in firmware update mode groupBoxRemote - 47 + 70 False @@ -483,7 +480,7 @@ red LED solid - in firmware update mode 50 - 266, 135 + 266, 132 80, 21 @@ -504,7 +501,7 @@ red LED solid - in firmware update mode groupBoxRemote - 48 + 71 False @@ -540,7 +537,7 @@ red LED solid - in firmware update mode 100 - 266, 162 + 266, 157 80, 21 @@ -561,7 +558,7 @@ red LED solid - in firmware update mode groupBoxRemote - 49 + 72 False @@ -588,7 +585,7 @@ red LED solid - in firmware update mode 250 - 266, 189 + 266, 182 80, 21 @@ -609,7 +606,7 @@ red LED solid - in firmware update mode groupBoxRemote - 50 + 73 False @@ -642,7 +639,7 @@ red LED solid - in firmware update mode 435000 - 266, 83 + 266, 82 80, 21 @@ -663,13 +660,13 @@ red LED solid - in firmware update mode groupBoxRemote - 51 + 74 False - 88, 243 + 88, 232 80, 21 @@ -677,9 +674,6 @@ red LED solid - in firmware update mode 111 - - AIR_SPEED is the inter-radio data rate in rounded kbps. So 128 means 128kbps. Max is 192, min is 2. I would not recommend values below 16 as the frequency hopping and tdm sync times get too long. - RMAVLINK @@ -690,7 +684,7 @@ red LED solid - in firmware update mode groupBoxRemote - 9 + 33 False @@ -699,17 +693,16 @@ red LED solid - in firmware update mode NoControl - 102, 323 + 148, 307 - 66, 20 + 20, 20 119 - OPPRESEND enables/disables "opportunistic resend". When enabled the radio will send a packet twice if the serial input buffer has less than 256 bytes in it. The 2nd send is marked as a resend and discarded by the receiving radio if it got the first packet OK. This makes a big difference to the link quality, especially for uplink commands. - + GPI1_1R/COUT Sets GPIO 1.1 as R/C(PPM) output RGPO1_1R_COUT @@ -721,7 +714,7 @@ red LED solid - in firmware update mode groupBoxRemote - 10 + 34 False @@ -730,17 +723,16 @@ red LED solid - in firmware update mode NoControl - 88, 297 + 148, 282 - 80, 20 + 20, 20 115 - OPPRESEND enables/disables "opportunistic resend". When enabled the radio will send a packet twice if the serial input buffer has less than 256 bytes in it. The 2nd send is marked as a resend and discarded by the receiving radio if it got the first packet OK. This makes a big difference to the link quality, especially for uplink commands. - + GPI1_1R/CIN Sets GPIO 1.1 as R/C(PPM) input RGPI1_1R_CIN @@ -752,7 +744,7 @@ red LED solid - in firmware update mode groupBoxRemote - 13 + 37 False @@ -785,7 +777,7 @@ red LED solid - in firmware update mode 1 - 88, 109 + 88, 107 80, 21 @@ -807,7 +799,7 @@ red LED solid - in firmware update mode groupBoxRemote - 14 + 38 False @@ -852,7 +844,7 @@ red LED solid - in firmware update mode 2 - 88, 136 + 88, 132 80, 21 @@ -873,7 +865,7 @@ red LED solid - in firmware update mode groupBoxRemote - 15 + 39 False @@ -969,7 +961,7 @@ red LED solid - in firmware update mode 30 - 88, 163 + 88, 157 80, 21 @@ -990,7 +982,7 @@ red LED solid - in firmware update mode groupBoxRemote - 16 + 40 False @@ -1020,7 +1012,7 @@ red LED solid - in firmware update mode 20 - 88, 191 + 88, 182 80, 21 @@ -1042,7 +1034,7 @@ red LED solid - in firmware update mode groupBoxRemote - 18 + 42 False @@ -1051,7 +1043,7 @@ red LED solid - in firmware update mode NoControl - 88, 217 + 88, 207 80, 20 @@ -1073,7 +1065,7 @@ red LED solid - in firmware update mode groupBoxRemote - 20 + 44 False @@ -1082,10 +1074,10 @@ red LED solid - in firmware update mode NoControl - 88, 271 + 148, 257 - 80, 20 + 20, 20 120 @@ -1104,7 +1096,7 @@ red LED solid - in firmware update mode groupBoxRemote - 23 + 47 False @@ -1113,7 +1105,7 @@ red LED solid - in firmware update mode NoControl - 274, 216 + 274, 207 80, 20 @@ -1134,7 +1126,7 @@ red LED solid - in firmware update mode groupBoxLocal - 43 + 66 False @@ -1233,7 +1225,7 @@ red LED solid - in firmware update mode 30 - 274, 243 + 274, 232 80, 21 @@ -1241,9 +1233,6 @@ red LED solid - in firmware update mode 92 - - Node ID. Base node ID is 0. One node must be acting as a base for a multipoint environment to work. - MAX_WINDOW @@ -1254,7 +1243,7 @@ red LED solid - in firmware update mode groupBoxLocal - 48 + 71 False @@ -1308,7 +1297,7 @@ red LED solid - in firmware update mode groupBoxLocal - 50 + 73 False @@ -1371,7 +1360,7 @@ red LED solid - in firmware update mode 50 - 274, 134 + 274, 132 80, 21 @@ -1392,7 +1381,7 @@ red LED solid - in firmware update mode groupBoxLocal - 51 + 74 False @@ -1419,7 +1408,7 @@ red LED solid - in firmware update mode 250 - 274, 188 + 274, 182 80, 21 @@ -1440,7 +1429,7 @@ red LED solid - in firmware update mode groupBoxLocal - 53 + 76 False @@ -1503,7 +1492,7 @@ red LED solid - in firmware update mode groupBoxLocal - 55 + 78 False @@ -1539,7 +1528,7 @@ red LED solid - in firmware update mode 100 - 274, 161 + 274, 157 80, 21 @@ -1560,7 +1549,7 @@ red LED solid - in firmware update mode groupBoxLocal - 58 + 81 False @@ -1569,17 +1558,16 @@ red LED solid - in firmware update mode NoControl - 110, 323 + 154, 307 - 66, 20 + 23, 20 129 - OPPRESEND enables/disables "opportunistic resend". When enabled the radio will send a packet twice if the serial input buffer has less than 256 bytes in it. The 2nd send is marked as a resend and discarded by the receiving radio if it got the first packet OK. This makes a big difference to the link quality, especially for uplink commands. - + GPI1_1R/COUT Sets GPIO 1.1 as R/C(PPM) output GPO1_1R_COUT @@ -1591,7 +1579,7 @@ red LED solid - in firmware update mode groupBoxLocal - 10 + 34 False @@ -1600,17 +1588,16 @@ red LED solid - in firmware update mode NoControl - 96, 297 + 154, 282 - 80, 20 + 23, 20 127 - OPPRESEND enables/disables "opportunistic resend". When enabled the radio will send a packet twice if the serial input buffer has less than 256 bytes in it. The 2nd send is marked as a resend and discarded by the receiving radio if it got the first packet OK. This makes a big difference to the link quality, especially for uplink commands. - + GPI1_1R/CIN Sets GPIO 1.1 as R/C(PPM) input GPI1_1R_CIN @@ -1622,13 +1609,13 @@ red LED solid - in firmware update mode groupBoxLocal - 12 + 36 False - 96, 244 + 96, 232 80, 21 @@ -1637,7 +1624,7 @@ red LED solid - in firmware update mode 126 - AIR_SPEED is the inter-radio data rate in rounded kbps. So 128 means 128kbps. Max is 192, min is 2. I would not recommend values below 16 as the frequency hopping and tdm sync times get too long. + Mavlink MAVLINK @@ -1649,7 +1636,7 @@ red LED solid - in firmware update mode groupBoxLocal - 13 + 37 False @@ -1682,7 +1669,7 @@ red LED solid - in firmware update mode 1 - 96, 109 + 96, 107 80, 21 @@ -1704,7 +1691,7 @@ red LED solid - in firmware update mode groupBoxLocal - 15 + 39 False @@ -1749,7 +1736,7 @@ red LED solid - in firmware update mode 2 - 96, 136 + 96, 132 80, 21 @@ -1770,7 +1757,7 @@ red LED solid - in firmware update mode groupBoxLocal - 18 + 42 False @@ -1866,7 +1853,7 @@ red LED solid - in firmware update mode 30 - 96, 163 + 96, 157 80, 21 @@ -1887,7 +1874,7 @@ red LED solid - in firmware update mode groupBoxLocal - 20 + 44 False @@ -1917,7 +1904,7 @@ red LED solid - in firmware update mode 20 - 96, 191 + 96, 182 80, 21 @@ -1939,7 +1926,7 @@ red LED solid - in firmware update mode groupBoxLocal - 22 + 46 False @@ -1948,7 +1935,7 @@ red LED solid - in firmware update mode NoControl - 96, 217 + 96, 207 80, 20 @@ -1970,7 +1957,7 @@ red LED solid - in firmware update mode groupBoxLocal - 24 + 48 False @@ -1979,10 +1966,10 @@ red LED solid - in firmware update mode NoControl - 96, 271 + 154, 257 - 80, 20 + 23, 20 124 @@ -2001,710 +1988,1832 @@ red LED solid - in firmware update mode groupBoxLocal - 27 + 51 - - 60, 12 + + False - - 147, 20 + + 0 - - 33 + + 1 - - RTI + + 2 - - System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 3 - - groupBoxRemote + + 4 - - 58 + + 5 - - 59, 12 + + 6 - - 147, 20 + + 7 - - 32 + + 8 - - ATI + + 9 - - System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 10 - - groupBoxLocal + + 11 - - 62 + + 12 - - True + + 13 - - NoControl + + 14 - - 14, 15 + + 15 - - 42, 13 + + 16 - - 36 + + 17 - - Version + + 18 - - label11 + + 19 - - System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 20 - - groupBoxLocal + + 21 - - 61 + + 22 - - True + + 23 - - NoControl + + 24 - - 18, 50 + + 25 - - 32, 13 + + 26 - - 37 + + 27 - - RSSI + + 28 - - label12 + + 29 - - System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 30 - - groupBoxLocal + + 274, 257 - - 64 + + 80, 21 - - NoControl + + 154 - - 9, 412 + + AES Encryption Level - - 442, 28 + + ENCRYPTION_LEVEL - - 3 + + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - NOTE: Always click "Copy required to remote" when modifying -this ensures you wont lose radio link + + groupBoxLocal - - lbl_status + + 10 - - System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + False - - $this + + 0 - - 11 + + 1 - - 207, 12 + + 2 - - 102, 20 + + 3 - - 79 + + 4 - - ATI3 + + 5 - - System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 6 - - groupBoxLocal + + 7 - - 60 + + 8 - - btnRandom + + 9 - - System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 10 - - groupBoxLocal + + 11 - - 0 + + 12 - - lblRX_ENCAP_METHOD + + 13 - - System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 14 - - groupBoxLocal + + 15 - - 1 + + 16 - + + 17 + + + 18 + + + 19 + + + 20 + + + 21 + + + 22 + + + 23 + + + 24 + + + 25 + + + 26 + + + 27 + + + 28 + + + 29 + + + 30 + + + 266, 257 + + + 80, 21 + + + 160 + + + AES Encryption Level + + + RENCRYPTION_LEVEL + + + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxRemote + + + 10 + + + False + + + 447, 403 + + + 80, 21 + + + 161 + + + The number of output frames for SBUS or PPM missing before enters failsafe. + + + FSFRAMELOSS + + + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxLocal + + + 3 + + + False + + + 447, 403 + + + 80, 21 + + + 167 + + + The number of output frames for SBUS or PPM missing before enters failsafe. + + + RFSFRAMELOSS + + + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxRemote + + + 3 + + + False + + + NoControl + + + 154, 332 + + + 23, 20 + + + 141 + + + GPO1_3SBUSIN + + + System.Windows.Forms.CheckBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxLocal + + + 23 + + + False + + + NoControl + + + 148, 332 + + + 36, 20 + + + 149 + + + RGPO1_3SBUSIN + + + System.Windows.Forms.CheckBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxRemote + + + 21 + + + False + + + NoControl + + + 154, 382 + + + 23, 20 + + + 151 + + + GPO1_3STATLED + + + System.Windows.Forms.CheckBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxLocal + + + 13 + + + False + + + NoControl + + + 283, 382 + + + 23, 20 + + + 153 + + + GPO1_0TXEN485 + + + System.Windows.Forms.CheckBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxLocal + + + 11 + + + False + + + NoControl + + + 275, 382 + + + 23, 20 + + + 159 + + + RGPO1_0TXEN485 + + + System.Windows.Forms.CheckBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxRemote + + + 11 + + + False + + + NoControl + + + 148, 382 + + + 23, 20 + + + 157 + + + RGPO1_3STATLED + + + System.Windows.Forms.CheckBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxRemote + + + 13 + + + 60, 12 + + + 147, 20 + + + 33 + + + RTI + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxRemote + + + 81 + + + 59, 12 + + + 147, 20 + + + 32 + + + ATI + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxLocal + + + 85 + + + True + + + NoControl + + + 14, 15 + + + 42, 13 + + + 36 + + + Version + + + label11 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxLocal + + + 84 + + + True + + + NoControl + + + 18, 50 + + + 32, 13 + + + 37 + + + RSSI + + + label12 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxLocal + + + 87 + + + NoControl + + + 9, 518 + + + 442, 28 + + + 3 + + + NOTE: Always click "Copy required to remote" when modifying +this ensures you wont lose radio link + + + lbl_status + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 10 + + + 207, 12 + + + 102, 20 + + + 79 + + + ATI3 + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxLocal + + + 83 + + + btnLoadFromFile + + + MissionPlanner.Controls.MyButton, RFD900Tools, Version=0.2.63.0, Culture=neutral, PublicKeyToken=null + + + groupBoxLocal + + + 0 + + + btnSaveToFile + + + MissionPlanner.Controls.MyButton, RFD900Tools, Version=0.2.63.0, Culture=neutral, PublicKeyToken=null + + + groupBoxLocal + + + 1 + + + label54 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxLocal + + + 2 + + + GPO1_3AUXOUT + + + System.Windows.Forms.CheckBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxLocal + + + 4 + + + lblGPO1_3AUXOUT + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxLocal + + + 5 + + + GPI1_2AUXIN + + + System.Windows.Forms.CheckBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxLocal + + + 6 + + + lblGPI1_2AUXIN + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxLocal + + + 7 + + + lblGPIO1_1FUNC + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxLocal + + + 8 + + + GPIO1_1FUNC + + + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxLocal + + + 9 + + + lblGPO1_0TXEN485 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxLocal + + + 12 + + + lblGPO1_3STATLED + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxLocal + + + 14 + + + GPO1_3SBUSOUT + + + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxLocal + + + 15 + + + label49 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxLocal + + + 16 + + + BUT_SetPPMFailSafe + + + MissionPlanner.Controls.MyButton, RFD900Tools, Version=0.2.63.0, Culture=neutral, PublicKeyToken=null + + + groupBoxLocal + + + 17 + + + txtCountry + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxLocal + + + 18 + + + label45 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxLocal + + + 19 + + + RATE_FREQBAND + + + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxLocal + + + 20 + + + lblSBUSOUT + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxLocal + + + 21 + + + lblSBUSIN + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxLocal + + + 22 + + + btnRandom + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxLocal + + + 24 + + + lblRX_ENCAP_METHOD + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxLocal + + + 25 + + RX_ENCAP_METHOD - + + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxLocal + + + 26 + + + lblTX_ENCAP_METHOD + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxLocal + + + 27 + + + TX_ENCAP_METHOD + + + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxLocal + + + 28 + + + lblDESTID + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxLocal + + + 29 + + + lblNODEID + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxLocal + + + 30 + + + DESTID + + + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxLocal + + + 31 + + + NODEID + + + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxLocal + + + 32 + + + lblGPO1_1R_COUT + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxLocal + + + 33 + + + lblGPI1_1R_CIN + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxLocal + + + 35 + + + label2 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxLocal + + + 38 + + + label1 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxLocal + + + 40 + + + FORMAT + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxLocal + + + 41 + + + label3 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxLocal + + + 43 + + + lblNETID + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxLocal + + + 45 + + + lblTXPOWER + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxLocal + + + 47 + + + lblECC + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxLocal + + + 49 + + + lblOPPRESEND + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxLocal + + + 50 + + + lblMAVLINK + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxLocal + + + 52 + + + lblANT_MODE + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxLocal + + + 53 + + + ANT_MODE + + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxLocal - - 2 + + 54 - - lblTX_ENCAP_METHOD + + lblSER_BRK_DETMS - + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxLocal - - 3 + + 55 - - TX_ENCAP_METHOD + + lblGLOBAL_RETRIES - + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxLocal + + + 56 + + + lblMAX_RETRIES + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxLocal + + + 57 + + + SER_BRK_DETMS + + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + + groupBoxLocal + + + 58 + + + GLOBAL_RETRIES + + + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxLocal + + + 59 + + + MAX_RETRIES + + + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxLocal + + + 60 + + + MAX_DATA + + + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxLocal + + + 61 + + + lblMAX_DATA + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxLocal + + + 62 + + + lblENCRYPTION_LEVEL + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxLocal + + + 63 + + + label35 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxLocal + + + 64 + + + txt_aeskey + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxLocal + + + 65 + + + lblRTSCTS + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxLocal + + + 67 + + + linkLabel_mavlink + + + System.Windows.Forms.LinkLabel, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + groupBoxLocal + + + 68 + + + linkLabel_lowlatency + + + System.Windows.Forms.LinkLabel, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + groupBoxLocal - - 4 + + 69 - - lblDESTID + + lblMAX_WINDOW - + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxLocal - - 5 + + 70 - - lblNODEID + + lblMIN_FREQ - + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxLocal - - 6 + + 72 - - DESTID + + lblLBT_RSSI - - System.Windows.Forms.ComboBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxLocal - - 7 + + 75 - - NODEID + + lblDUTY_CYCLE - - System.Windows.Forms.ComboBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxLocal - - 8 + + 77 - - label40 + + lblNUM_CHANNELS - + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxLocal - - 9 + + 79 - - label39 + + lblMAX_FREQ - + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxLocal - - 11 + + 80 - - label2 + + ATI2 - - System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxLocal - - 14 + + 82 - - label1 + + 12, 48 - - System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 533, 463 - + + 80 + + + Local + + groupBoxLocal - - 16 + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - FORMAT + + $this - - System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 6 - - groupBoxLocal + + True - - 17 + + NoControl - - label3 + + 427, 387 - - System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 100, 13 - - groupBoxLocal + + 162 - - 19 + + Failsafe Frame Loss - - label4 + + label54 - + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxLocal - - 21 + + 2 - - label5 + + False - - System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + NoControl - - groupBoxLocal + + 154, 432 - - 23 + + 23, 20 - - label6 + + 160 - - System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + GPO1_3AUXOUT + + + System.Windows.Forms.CheckBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxLocal - - 25 + + 4 + + + True - - label8 + + NoControl - - System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 18, 436 - - groupBoxLocal + + 93, 13 - - 26 + + 159 + + + GPO1_3AUXOUT - - label7 + + lblGPO1_3AUXOUT - + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxLocal - - 28 + + 5 - - lblANT_MODE + + False - - System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + NoControl - - groupBoxLocal + + 154, 407 - - 29 + + 23, 20 - - ANT_MODE + + 158 - - System.Windows.Forms.ComboBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + GPI1_2AUXIN - + + System.Windows.Forms.CheckBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + groupBoxLocal - - 30 + + 6 - - lblSER_BRK_DETMS + + True - - System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + NoControl - - groupBoxLocal + + 18, 411 - - 31 + + 76, 13 - - lblGLOBAL_RETRIES + + 157 - + + GPI1_2AUXIN + + + lblGPI1_2AUXIN + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxLocal - - 32 + + 7 - - lblMAX_RETRIES + + True - - System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + NoControl - - groupBoxLocal + + 182, 411 - - 33 + + 80, 13 - - SER_BRK_DETMS + + 156 - - System.Windows.Forms.ComboBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + GPIO1_1FUNC - + + lblGPIO1_1FUNC + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + groupBoxLocal - - 34 + + 8 - - GLOBAL_RETRIES + + False - - System.Windows.Forms.ComboBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 274, 406 - - groupBoxLocal + + 80, 21 - - 35 + + 155 - - MAX_RETRIES + + GPIO1_1FUNC - + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxLocal - - 36 + + 9 - - MAX_DATA + + True - - System.Windows.Forms.ComboBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + NoControl - - groupBoxLocal + + 182, 386 - - 37 + + 95, 13 - - lblMAX_DATA + + 152 - + + GPO1_0TXEN485 + + + lblGPO1_0TXEN485 + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxLocal - - 38 + + 12 - - ENCRYPTION_LEVEL + + True - - System.Windows.Forms.CheckBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + NoControl - - groupBoxLocal + + 18, 386 - - 39 + + 97, 13 + + + 150 - - label36 + + GPO1_3STATLED - + + lblGPO1_3STATLED + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxLocal - - 40 + + 14 - - label35 + + False - - System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 425, 332 - - groupBoxLocal + + 102, 21 - - 41 + + 149 - - txt_aeskey + + GPO1_3SBUSOUT - - System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxLocal - - 42 + + 15 + + + True + + + NoControl + + + 377, 18 + + + 46, 13 + + + 148 - - label19 + + Country: - + + label49 + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxLocal - - 44 + + 16 - - linkLabel_mavlink + + 380, 38 - - System.Windows.Forms.LinkLabel, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + True - - groupBoxLocal + + 61, 39 - - 45 + + 147 - - linkLabel_lowlatency + + txtCountry - - System.Windows.Forms.LinkLabel, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxLocal - - 46 + + 18 - - label18 + + True - - System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + NoControl - - groupBoxLocal + + 18, 361 - - 47 + + 81, 13 + + + 146 - - label13 + + Rate/FreqBand - + + label45 + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxLocal - - 49 + + 19 - - label17 + + False - - System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 128, 357 - - groupBoxLocal + + 399, 21 - - 52 + + 145 - - label16 + + RATE_FREQBAND - - System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxLocal - - 54 + + 20 + + + True - - label15 + + NoControl - - System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 360, 311 - - groupBoxLocal + + 103, 13 - - 56 + + 144 + + + GPO1_3SBUSOUT: - - label14 + + lblSBUSOUT - + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxLocal - - 57 + + 21 - - ATI2 + + True - - System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + NoControl - - groupBoxLocal + + 18, 336 - - 59 + + 88, 13 - - 12, 48 + + 142 - - 533, 361 + + GPO1_3SBUSIN - - 80 + + lblSBUSIN - - Local + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxLocal - - System.Windows.Forms.GroupBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - $this - - - 7 + + 22 False @@ -2713,10 +3822,10 @@ this ensures you wont lose radio link NoControl - 242, 291 + 242, 282 - 111, 24 + 113, 24 140 @@ -2734,7 +3843,7 @@ this ensures you wont lose radio link groupBoxLocal - 0 + 24 True @@ -2743,7 +3852,7 @@ this ensures you wont lose radio link NoControl - 360, 196 + 360, 186 58, 13 @@ -2767,13 +3876,13 @@ this ensures you wont lose radio link groupBoxLocal - 1 + 25 False - 447, 193 + 447, 182 80, 21 @@ -2794,7 +3903,7 @@ this ensures you wont lose radio link groupBoxLocal - 2 + 26 True @@ -2803,7 +3912,7 @@ this ensures you wont lose radio link NoControl - 360, 168 + 360, 161 57, 13 @@ -2827,13 +3936,13 @@ this ensures you wont lose radio link groupBoxLocal - 3 + 27 False - 447, 165 + 447, 157 80, 21 @@ -2854,7 +3963,7 @@ this ensures you wont lose radio link groupBoxLocal - 4 + 28 True @@ -2863,7 +3972,7 @@ this ensures you wont lose radio link NoControl - 360, 140 + 360, 136 43, 13 @@ -2887,7 +3996,7 @@ this ensures you wont lose radio link groupBoxLocal - 5 + 29 True @@ -2896,7 +4005,7 @@ this ensures you wont lose radio link NoControl - 360, 112 + 360, 111 47, 13 @@ -2920,13 +4029,13 @@ this ensures you wont lose radio link groupBoxLocal - 6 + 30 False - 447, 137 + 447, 132 80, 21 @@ -2947,13 +4056,13 @@ this ensures you wont lose radio link groupBoxLocal - 7 + 31 False - 447, 109 + 447, 107 80, 21 @@ -2974,67 +4083,67 @@ this ensures you wont lose radio link groupBoxLocal - 8 + 32 - + True - + NoControl - - 18, 326 + + 18, 311 - - 86, 13 + + 91, 13 - + 130 - - GPI1_1R/COUT + + GPO1_1R/COUT - - label40 + + lblGPO1_1R_COUT - + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxLocal - - 9 + + 33 - + True - + NoControl - - 18, 300 + + 18, 286 - + 74, 13 - + 128 - + GPI1_1R/CIN - - label39 + + lblGPI1_1R_CIN - + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxLocal - - 11 + + 35 True @@ -3043,7 +4152,7 @@ this ensures you wont lose radio link NoControl - 18, 87 + 18, 86 39, 13 @@ -3064,7 +4173,7 @@ this ensures you wont lose radio link groupBoxLocal - 14 + 38 True @@ -3073,7 +4182,7 @@ this ensures you wont lose radio link NoControl - 18, 113 + 18, 111 32, 13 @@ -3094,10 +4203,10 @@ this ensures you wont lose radio link groupBoxLocal - 16 + 40 - 96, 83 + 96, 82 80, 20 @@ -3115,7 +4224,7 @@ this ensures you wont lose radio link groupBoxLocal - 17 + 41 True @@ -3124,7 +4233,7 @@ this ensures you wont lose radio link NoControl - 18, 139 + 18, 136 53, 13 @@ -3145,157 +4254,157 @@ this ensures you wont lose radio link groupBoxLocal - 19 + 43 - + True - + NoControl - - 18, 166 + + 18, 161 - + 38, 13 - + 118 - + Net ID - - label4 + + lblNETID - + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxLocal - - 21 + + 45 - + True - + NoControl - - 18, 193 + + 18, 186 - + 52, 13 - + 120 - + Tx Power - - label5 + + lblTXPOWER - + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxLocal - - 23 + + 47 - + True - + NoControl - - 18, 218 + + 18, 211 - + 28, 13 - + 122 - + ECC - - label6 + + lblECC - + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxLocal - - 25 + + 49 - + True - + NoControl - - 18, 274 + + 18, 261 - + 61, 13 - + 125 - + Op Resend - - label8 + + lblOPPRESEND - + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxLocal - - 26 + + 50 - + True - + NoControl - - 18, 247 + + 18, 236 - + 44, 13 - + 123 - + Mavlink - - label7 + + lblMAVLINK - + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxLocal - - 28 + + 52 True @@ -3304,7 +4413,7 @@ this ensures you wont lose radio link NoControl - 362, 335 + 360, 86 77, 13 @@ -3315,9 +4424,6 @@ this ensures you wont lose radio link Antenna Mode - - False - lblANT_MODE @@ -3328,13 +4434,13 @@ this ensures you wont lose radio link groupBoxLocal - 29 + 53 False - 447, 335 + 447, 82 80, 21 @@ -3342,9 +4448,6 @@ this ensures you wont lose radio link 109 - - False - ANT_MODE @@ -3355,7 +4458,7 @@ this ensures you wont lose radio link groupBoxLocal - 30 + 54 True @@ -3364,7 +4467,7 @@ this ensures you wont lose radio link NoControl - 360, 310 + 360, 286 84, 13 @@ -3388,7 +4491,7 @@ this ensures you wont lose radio link groupBoxLocal - 31 + 55 True @@ -3397,7 +4500,7 @@ this ensures you wont lose radio link NoControl - 360, 282 + 360, 261 73, 13 @@ -3421,7 +4524,7 @@ this ensures you wont lose radio link groupBoxLocal - 32 + 56 True @@ -3430,7 +4533,7 @@ this ensures you wont lose radio link NoControl - 360, 254 + 360, 236 63, 13 @@ -3454,13 +4557,13 @@ this ensures you wont lose radio link groupBoxLocal - 33 + 57 False - 447, 307 + 447, 282 80, 21 @@ -3481,13 +4584,13 @@ this ensures you wont lose radio link groupBoxLocal - 34 + 58 False - 447, 279 + 447, 257 80, 21 @@ -3508,13 +4611,13 @@ this ensures you wont lose radio link groupBoxLocal - 35 + 59 False - 447, 251 + 447, 232 80, 21 @@ -3535,13 +4638,13 @@ this ensures you wont lose radio link groupBoxLocal - 36 + 60 False - 447, 223 + 447, 207 80, 21 @@ -3562,7 +4665,7 @@ this ensures you wont lose radio link groupBoxLocal - 37 + 61 True @@ -3571,7 +4674,7 @@ this ensures you wont lose radio link NoControl - 360, 226 + 360, 211 53, 13 @@ -3595,64 +4698,37 @@ this ensures you wont lose radio link groupBoxLocal - 38 - - - False - - - NoControl - - - 274, 270 - - - 80, 20 - - - 99 - - - ENCRYPTION_LEVEL - - - System.Windows.Forms.CheckBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - groupBoxLocal - - - 39 + 62 - + True - + NoControl - - 181, 271 + + 181, 261 - + 81, 13 - + 100 - + AES Encryption - - label36 + + lblENCRYPTION_LEVEL - + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxLocal - - 40 + + 63 True @@ -3661,7 +4737,7 @@ this ensures you wont lose radio link NoControl - 182, 299 + 182, 286 49, 13 @@ -3682,13 +4758,13 @@ this ensures you wont lose radio link groupBoxLocal - 41 + 64 - 180, 316 + 180, 307 - 32 + 64 174, 20 @@ -3706,37 +4782,37 @@ this ensures you wont lose radio link groupBoxLocal - 42 + 65 - + True - + NoControl - - 181, 217 + + 181, 211 - + 53, 13 - + 97 - + RTS CTS - - label19 + + lblRTSCTS - + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxLocal - - 44 + + 67 True @@ -3745,7 +4821,7 @@ this ensures you wont lose radio link NoControl - 183, 333 + 183, 327 146, 13 @@ -3766,7 +4842,7 @@ this ensures you wont lose radio link groupBoxLocal - 45 + 68 True @@ -3775,7 +4851,7 @@ this ensures you wont lose radio link NoControl - 182, 346 + 182, 340 124, 13 @@ -3796,187 +4872,187 @@ this ensures you wont lose radio link groupBoxLocal - 46 + 69 - + True - + NoControl - - 182, 246 + + 182, 236 - + 91, 13 - + 93 - + Max Window (ms) - - label18 + + lblMAX_WINDOW - + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxLocal - - 47 + + 70 - + True - + NoControl - + 182, 86 - + 48, 13 - + 87 - + Min Freq - - label13 + + lblMIN_FREQ - + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxLocal - - 49 + + 72 - + True - + NoControl - - 182, 191 + + 182, 186 - + 50, 13 - + 91 - + LBT Rssi - - label17 + + lblLBT_RSSI - + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxLocal - - 52 + + 75 - + True - + NoControl - - 182, 164 + + 182, 161 - + 58, 13 - + 90 - + Duty Cycle - - label16 + + lblDUTY_CYCLE - + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxLocal - - 54 + + 77 - + True - + NoControl - - 182, 138 + + 182, 136 - + 73, 13 - + 89 - + # of Channels - - label15 + + lblNUM_CHANNELS - + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxLocal - - 56 + + 79 - + True - + NoControl - - 182, 110 + + 182, 111 - + 51, 13 - + 88 - + Max Freq - - label14 + + lblMAX_FREQ - + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxLocal - - 57 + + 80 315, 12 @@ -4000,535 +5076,550 @@ this ensures you wont lose radio link groupBoxLocal - 59 - - - lblRRX_ENCAP_METHOD - - - System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + 82 - - groupBoxRemote + + False - - 0 + + NoControl - - RRX_ENCAP_METHOD + + 209, 428 - - System.Windows.Forms.ComboBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 102, 29 - - groupBoxRemote + + 170 - - 1 + + Load from File... - - lblRTX_ENCAP_METHOD + + btnRemoteLoadFromFile - - System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + MissionPlanner.Controls.MyButton, RFD900Tools, Version=0.2.63.0, Culture=neutral, PublicKeyToken=null - + groupBoxRemote - - 2 - - - RTX_ENCAP_METHOD - - - System.Windows.Forms.ComboBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 0 - - groupBoxRemote + + False - - 3 + + NoControl - - lblRDESTID + + 317, 428 - - System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 102, 29 - - groupBoxRemote + + 169 - - 4 + + Save to File... - - lblRNODEID + + btnRemoteSaveToFile - - System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + MissionPlanner.Controls.MyButton, RFD900Tools, Version=0.2.63.0, Culture=neutral, PublicKeyToken=null - + groupBoxRemote - - 5 - - - RDESTID - - - System.Windows.Forms.ComboBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - groupBoxRemote + + 1 - - 6 + + True - - RNODEID + + NoControl - - System.Windows.Forms.ComboBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 427, 387 - - groupBoxRemote + + 100, 13 - - 7 + + 168 - - label41 + + Failsafe Frame Loss - + + lblRFSFRAMELOSS + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxRemote - - 8 + + 2 - - RFORMAT + + False - - System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + NoControl - - groupBoxRemote + + 148, 432 - - 11 + + 23, 20 - - label42 + + 166 - - System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + RGPO1_3AUXOUT - + + System.Windows.Forms.CheckBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + groupBoxRemote - - 12 + + 4 - - label25 + + True - - System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + NoControl - - groupBoxRemote + + 10, 436 - - 17 + + 93, 13 + + + 165 - - label26 + + GPO1_3AUXOUT - + + lblRGPO1_3AUXOUT + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxRemote - - 19 + + 5 - - label27 + + False - - System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + NoControl - - groupBoxRemote + + 148, 407 - - 21 + + 23, 20 - - label28 + + 164 - - System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + RGPI1_2AUXIN + + + System.Windows.Forms.CheckBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxRemote - - 22 + + 6 + + + True - - label29 + + NoControl - - System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 10, 411 - - groupBoxRemote + + 76, 13 - - 24 + + 163 - - label32 + + GPI1_2AUXIN - + + lblRGPI1_2AUXIN + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxRemote - - 25 + + 7 - - label30 + + True - - System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + NoControl - - groupBoxRemote + + 174, 408 - - 26 + + 80, 13 - - label31 + + 162 - + + GPIO1_1FUNC + + + lblRGPIO1_1FUNC + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxRemote - - 27 + + 8 - - lblRANT_MODE + + False - - System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 266, 405 - - groupBoxRemote + + 80, 21 - - 28 + + 161 - - RANT_MODE + + RGPIO1_1FUNC - + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxRemote - - 29 - - - lblRSER_BRK_DETMS - - - System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 9 - - groupBoxRemote + + True - - 30 + + NoControl - - lblRGLOBAL_RETRIES + + 174, 386 - - System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 95, 13 - - groupBoxRemote + + 158 - - 31 + + GPO1_0TXEN485 - - lblRMAX_RETRIES + + lblRGPO1_0TXEN485 - + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxRemote - - 32 + + 12 - - RSER_BRK_DETMS + + True - - System.Windows.Forms.ComboBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + NoControl - - groupBoxRemote + + 10, 386 - - 33 + + 97, 13 - - RGLOBAL_RETRIES + + 156 - - System.Windows.Forms.ComboBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + GPO1_3STATLED - + + lblRGPO1_3STATLED + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + groupBoxRemote - - 34 + + 14 - - RMAX_RETRIES + + False - - System.Windows.Forms.ComboBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 425, 332 - - groupBoxRemote + + 102, 21 - - 35 + + 155 - - RMAX_DATA + + RGPO1_3SBUSOUT - + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxRemote - - 36 - - - lblRMAX_DATA + + 15 - - System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + False - - groupBoxRemote + + NoControl - - 37 + + 425, 428 - - label38 + + 102, 29 - - System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 87 - - groupBoxRemote + + Set PPM Fail Safe - - 38 + + Record default PPM stream for PPM output (vehicle side) - - RENCRYPTION_LEVEL + + BUT_SetPPMFailSafeRemote - - System.Windows.Forms.CheckBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + MissionPlanner.Controls.MyButton, RFD900Tools, Version=0.2.63.0, Culture=neutral, PublicKeyToken=null - + groupBoxRemote - - 39 - - - txt_Raeskey - - - System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 16 - - groupBoxRemote + + True - - 40 + + NoControl - - label37 + + 364, 18 - - System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 46, 13 - - groupBoxRemote + + 154 - - 41 + + Country: - - label33 + + label50 - + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxRemote - - 43 + + 17 - - label34 + + 367, 38 - - System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + True - - groupBoxRemote + + 61, 39 - - 44 + + 153 - - label24 + + txtRCountry - - System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxRemote - - 46 + + 18 - - label23 + + True - - System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + NoControl - - groupBoxRemote + + 357, 311 - - 52 + + 103, 13 + + + 152 + + + GPO1_3SBUSOUT: - - label22 + + lblRSBUSOUT - + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxRemote - - 53 + + 19 - - label21 + + True - - System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + NoControl - - groupBoxRemote + + 10, 336 - - 54 + + 88, 13 + + + 150 + + + GPO1_3SBUSIN - - label20 + + lblRSBUSIN - + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxRemote - - 55 + + 20 - - RTI2 + + True - - System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + NoControl - - groupBoxRemote + + 10, 361 - - 56 + + 81, 13 - - label9 + + 148 - + + Rate/FreqBand + + + label46 + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxRemote - - 57 + + 22 - - 551, 48 + + False - - 533, 361 + + 121, 357 - - 81 + + 406, 21 - - Remote + + 147 - - groupBoxRemote + + RRATE_FREQBAND - - System.Windows.Forms.GroupBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - $this + + groupBoxRemote - - 6 + + 23 True @@ -4537,7 +5628,7 @@ this ensures you wont lose radio link NoControl - 352, 195 + 357, 186 58, 13 @@ -4561,10 +5652,10 @@ this ensures you wont lose radio link groupBoxRemote - 0 + 24 - 442, 188 + 447, 182 80, 21 @@ -4585,7 +5676,7 @@ this ensures you wont lose radio link groupBoxRemote - 1 + 25 True @@ -4594,7 +5685,7 @@ this ensures you wont lose radio link NoControl - 352, 167 + 357, 161 57, 13 @@ -4618,10 +5709,10 @@ this ensures you wont lose radio link groupBoxRemote - 2 + 26 - 442, 160 + 447, 157 80, 21 @@ -4642,7 +5733,7 @@ this ensures you wont lose radio link groupBoxRemote - 3 + 27 True @@ -4651,7 +5742,7 @@ this ensures you wont lose radio link NoControl - 352, 139 + 357, 136 43, 13 @@ -4675,7 +5766,7 @@ this ensures you wont lose radio link groupBoxRemote - 4 + 28 True @@ -4684,7 +5775,7 @@ this ensures you wont lose radio link NoControl - 352, 111 + 357, 111 47, 13 @@ -4708,10 +5799,10 @@ this ensures you wont lose radio link groupBoxRemote - 5 + 29 - 442, 132 + 447, 132 80, 21 @@ -4732,10 +5823,10 @@ this ensures you wont lose radio link groupBoxRemote - 6 + 30 - 442, 104 + 447, 107 80, 21 @@ -4756,40 +5847,40 @@ this ensures you wont lose radio link groupBoxRemote - 7 + 31 - + True - + NoControl - - 10, 326 + + 10, 311 - - 86, 13 + + 91, 13 - + 121 - - GPI1_1R/COUT + + GPO1_1R/COUT - - label41 + + lblRGPO1_1R_COUT - + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxRemote - - 8 + + 32 - 88, 83 + 88, 82 80, 20 @@ -4807,187 +5898,187 @@ this ensures you wont lose radio link groupBoxRemote - 11 + 35 - + True - + NoControl - - 10, 300 + + 10, 286 - + 74, 13 - + 118 - + GPI1_1R/CIN - - label42 + + lblRGPI1_1R_CIN - + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxRemote - - 12 + + 36 - + True - + NoControl - - 11, 274 + + 11, 261 - + 61, 13 - + 129 - + Op Resend - - label25 + + lblROPPRESEND - + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxRemote - - 17 + + 41 - + True - + NoControl - - 11, 247 + + 11, 236 - + 44, 13 - + 128 - + Mavlink - - label26 + + lblRMAVLINK - + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxRemote - - 19 + + 43 - + True - + NoControl - - 11, 220 + + 11, 211 - + 28, 13 - + 127 - + ECC - - label27 + + lblRECC - + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxRemote - - 21 + + 45 - + True - + NoControl - - 11, 193 + + 11, 186 - + 52, 13 - + 126 - + Tx Power - - label28 + + lblRTXPOWER - + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxRemote - - 22 + + 46 - + True - + NoControl - - 11, 166 + + 11, 161 - + 38, 13 - + 125 - + Net ID - - label29 + + lblRNETID - + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxRemote - - 24 + + 48 True @@ -4996,7 +6087,7 @@ this ensures you wont lose radio link NoControl - 11, 113 + 11, 111 32, 13 @@ -5017,7 +6108,7 @@ this ensures you wont lose radio link groupBoxRemote - 25 + 49 True @@ -5026,7 +6117,7 @@ this ensures you wont lose radio link NoControl - 11, 139 + 11, 136 53, 13 @@ -5047,7 +6138,7 @@ this ensures you wont lose radio link groupBoxRemote - 26 + 50 True @@ -5056,7 +6147,7 @@ this ensures you wont lose radio link NoControl - 11, 87 + 11, 86 39, 13 @@ -5077,7 +6168,7 @@ this ensures you wont lose radio link groupBoxRemote - 27 + 51 True @@ -5086,7 +6177,7 @@ this ensures you wont lose radio link NoControl - 354, 332 + 357, 86 77, 13 @@ -5097,9 +6188,6 @@ this ensures you wont lose radio link Antenna Mode - - False - lblRANT_MODE @@ -5110,10 +6198,13 @@ this ensures you wont lose radio link groupBoxRemote - 28 + 52 + + + False - 442, 328 + 447, 82 80, 21 @@ -5121,9 +6212,6 @@ this ensures you wont lose radio link 108 - - False - RANT_MODE @@ -5134,7 +6222,7 @@ this ensures you wont lose radio link groupBoxRemote - 29 + 53 True @@ -5143,7 +6231,7 @@ this ensures you wont lose radio link NoControl - 352, 307 + 357, 286 84, 13 @@ -5167,7 +6255,7 @@ this ensures you wont lose radio link groupBoxRemote - 30 + 54 True @@ -5176,7 +6264,7 @@ this ensures you wont lose radio link NoControl - 352, 279 + 357, 261 73, 13 @@ -5200,7 +6288,7 @@ this ensures you wont lose radio link groupBoxRemote - 31 + 55 True @@ -5209,7 +6297,7 @@ this ensures you wont lose radio link NoControl - 352, 251 + 357, 236 63, 13 @@ -5233,10 +6321,10 @@ this ensures you wont lose radio link groupBoxRemote - 32 + 56 - 442, 300 + 447, 282 80, 21 @@ -5257,10 +6345,10 @@ this ensures you wont lose radio link groupBoxRemote - 33 + 57 - 442, 272 + 447, 257 80, 21 @@ -5281,10 +6369,10 @@ this ensures you wont lose radio link groupBoxRemote - 34 + 58 - 442, 244 + 447, 232 80, 21 @@ -5305,10 +6393,10 @@ this ensures you wont lose radio link groupBoxRemote - 35 + 59 - 442, 216 + 447, 207 80, 21 @@ -5329,7 +6417,7 @@ this ensures you wont lose radio link groupBoxRemote - 36 + 60 True @@ -5338,7 +6426,7 @@ this ensures you wont lose radio link NoControl - 352, 223 + 357, 211 53, 13 @@ -5362,7 +6450,7 @@ this ensures you wont lose radio link groupBoxRemote - 37 + 61 True @@ -5371,7 +6459,7 @@ this ensures you wont lose radio link NoControl - 174, 300 + 174, 286 49, 13 @@ -5392,40 +6480,13 @@ this ensures you wont lose radio link groupBoxRemote - 38 - - - False - - - NoControl - - - 266, 271 - - - 80, 20 - - - 90 - - - RENCRYPTION_LEVEL - - - System.Windows.Forms.CheckBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - groupBoxRemote - - - 39 + 62 - 168, 316 + 168, 307 - 32 + 64 180, 20 @@ -5443,247 +6504,247 @@ this ensures you wont lose radio link groupBoxRemote - 40 + 63 - + True - + NoControl - - 173, 272 + + 173, 261 - + 81, 13 - + 92 - + AES Encryption - - label37 + + lblRENCRYPTION_LEVEL - + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxRemote - - 41 + + 64 - + True - + NoControl - - 173, 218 + + 173, 211 - + 53, 13 - + 99 - + RTS CTS - - label33 + + lblRRTSCTS - + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxRemote - - 43 + + 66 - + True - + NoControl - - 174, 247 + + 174, 236 - + 91, 13 - + 97 - + Max Window (ms) - - label34 + + lblRMAX_WINDOW - + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxRemote - - 44 + + 67 - + True - + NoControl - - 174, 87 + + 174, 86 - + 48, 13 - + 87 - + Min Freq - - label24 + + lblRMIN_FREQ - + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxRemote - - 46 + + 69 - + True - + NoControl - + 174, 111 - + 51, 13 - + 88 - + Max Freq - - label23 + + lblRMAX_FREQ - + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxRemote - - 52 + + 75 - + True - + NoControl - - 174, 139 + + 174, 136 - + 73, 13 - + 89 - + # of Channels - - label22 + + lblRNUM_CHANNELS - + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxRemote - - 53 + + 76 - + True - + NoControl - - 174, 165 + + 174, 161 - + 58, 13 - + 93 - + Duty Cycle - - label21 + + lblRDUTY_CYCLE - + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxRemote - - 54 + + 77 - + True - + NoControl - - 174, 192 + + 174, 186 - + 50, 13 - + 95 - + LBT Rssi - - label20 + + lblRLBT_RSSI - + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + groupBoxRemote - - 55 + + 78 300, 12 @@ -5707,7 +6768,7 @@ this ensures you wont lose radio link groupBoxRemote - 56 + 79 True @@ -5737,7 +6798,31 @@ this ensures you wont lose radio link groupBoxRemote - 57 + 80 + + + 551, 48 + + + 533, 463 + + + 81 + + + Remote + + + groupBoxRemote + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 5 True @@ -5764,40 +6849,19 @@ this ensures you wont lose radio link $this - 5 - - - False - - - NoControl - - - 551, 411 - - - 102, 29 - - - 86 - - - Set PPM Fail Safe - - - Record default PPM stream for PPM output (vehicle side) - - - BUT_SetPPMFailSafe - - - MissionPlanner.Controls.MyButton, SikRadio, Version=0.0.7428.29620, Culture=neutral, PublicKeyToken=null + 4 - - $this + + 114, 17 + + + INI Files|*.ini - - 1 + + 208, 17 + + + INI Files|*.ini NoControl @@ -5818,13 +6882,13 @@ this ensures you wont lose radio link BUT_loadcustom - MissionPlanner.Controls.MyButton, SikRadio, Version=0.0.7428.29620, Culture=neutral, PublicKeyToken=null + MissionPlanner.Controls.MyButton, RFD900Tools, Version=0.2.63.0, Culture=neutral, PublicKeyToken=null $this - 2 + 1 NoControl @@ -5845,13 +6909,46 @@ this ensures you wont lose radio link BUT_resettodefault - MissionPlanner.Controls.MyButton, SikRadio, Version=0.0.7428.29620, Culture=neutral, PublicKeyToken=null + MissionPlanner.Controls.MyButton, RFD900Tools, Version=0.2.63.0, Culture=neutral, PublicKeyToken=null $this - 3 + 2 + + + False + + + NoControl + + + 425, 428 + + + 102, 29 + + + 86 + + + Set PPM Fail Safe + + + Record default PPM stream for PPM output (vehicle side) + + + BUT_SetPPMFailSafe + + + MissionPlanner.Controls.MyButton, RFD900Tools, Version=0.2.63.0, Culture=neutral, PublicKeyToken=null + + + groupBoxLocal + + + 17 False @@ -5875,13 +6972,13 @@ this ensures you wont lose radio link BUT_savesettings - MissionPlanner.Controls.MyButton, SikRadio, Version=0.0.7428.29620, Culture=neutral, PublicKeyToken=null + MissionPlanner.Controls.MyButton, RFD900Tools, Version=0.2.63.0, Culture=neutral, PublicKeyToken=null $this - 9 + 8 NoControl @@ -5902,13 +6999,13 @@ this ensures you wont lose radio link BUT_getcurrent - MissionPlanner.Controls.MyButton, SikRadio, Version=0.0.7428.29620, Culture=neutral, PublicKeyToken=null + MissionPlanner.Controls.MyButton, RFD900Tools, Version=0.2.63.0, Culture=neutral, PublicKeyToken=null $this - 10 + 9 NoControl @@ -5925,17 +7022,20 @@ this ensures you wont lose radio link Upload Firmware (Local) + + False + BUT_upload - MissionPlanner.Controls.MyButton, SikRadio, Version=0.0.7428.29620, Culture=neutral, PublicKeyToken=null + MissionPlanner.Controls.MyButton, RFD900Tools, Version=0.2.63.0, Culture=neutral, PublicKeyToken=null $this - 12 + 11 False @@ -5944,7 +7044,7 @@ this ensures you wont lose radio link NoControl - 443, 412 + 457, 518 102, 29 @@ -5959,13 +7059,73 @@ this ensures you wont lose radio link BUT_Syncoptions - MissionPlanner.Controls.MyButton, SikRadio, Version=0.0.7428.29620, Culture=neutral, PublicKeyToken=null + MissionPlanner.Controls.MyButton, RFD900Tools, Version=0.2.63.0, Culture=neutral, PublicKeyToken=null $this - 8 + 7 + + + False + + + NoControl + + + 317, 428 + + + 102, 29 + + + 163 + + + Save to File... + + + btnSaveToFile + + + MissionPlanner.Controls.MyButton, RFD900Tools, Version=0.2.63.0, Culture=neutral, PublicKeyToken=null + + + groupBoxLocal + + + 1 + + + False + + + NoControl + + + 209, 428 + + + 102, 29 + + + 164 + + + Load from File... + + + btnLoadFromFile + + + MissionPlanner.Controls.MyButton, RFD900Tools, Version=0.2.63.0, Culture=neutral, PublicKeyToken=null + + + groupBoxLocal + + + 0 True @@ -5977,7 +7137,7 @@ this ensures you wont lose radio link 781, 492 - 1096, 492 + 1096, 608 toolTip1 @@ -5985,6 +7145,18 @@ this ensures you wont lose radio link System.Windows.Forms.ToolTip, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + dlgSave + + + System.Windows.Forms.SaveFileDialog, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + dlgOpen + + + System.Windows.Forms.OpenFileDialog, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + Sikradio diff --git a/Radio/Sikradio.zh-Hant.resx b/Radio/Sikradio.zh-Hant.resx index 1fc40fb9c6..00fad60940 100644 --- a/Radio/Sikradio.zh-Hant.resx +++ b/Radio/Sikradio.zh-Hant.resx @@ -117,6 +117,23 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 12, 389 + + + 755, 36 + + + + 2 + + + 17, 17 + + + False + 115 @@ -144,18 +161,75 @@ 1 + + 81, 29 + + + 80, 21 + + + 4 + 鮑率的單位為 kbps. 所以 57 表示為 57600. + + True + + + 3, 32 + + + 32, 13 + + + 5 + 鮑率 + + 81, 3 + + + 80, 20 + + + 7 + + + True + + + 3, 6 + + + 39, 13 + + + 8 + 格式版本 + + True + + + 3, 59 + + + 53, 13 + + + 10 + 速率(空中) + + False + 250 @@ -195,12 +269,36 @@ 2 + + 81, 56 + + + 80, 21 + + + 9 + 速率是無線資料傳輸率單位為 kbps。128 表示 128kbps。 最大值為 192,最小值為 2。我不建議這個頻率低於 16 這樣會造成 tdm 同步時間太久。 + + True + + + 3, 86 + + + 38, 13 + + + 12 + 網路 ID + + False + 1 @@ -291,12 +389,36 @@ 30 + + 81, 83 + + + 80, 21 + + + 11 + "網路 ID" 由 16 位元組成。這被用於發送跳頻順序並確保資料來自正確的無線電。請確保您和其他人是使用不同的"網路 ID" 除非你想要在同一個區域使用相同網域的無線電。 + + True + + + 3, 113 + + + 52, 13 + + + 14 + 發射功率 + + False + 1 @@ -321,32 +443,128 @@ 20 + + 81, 110 + + + 80, 21 + + + 13 + 發射功率的單位為 dBm。 20dBm 就是 100mW。這個值的設定對於較低階的短距離測試非常有用。 + + True + + + 3, 138 + + + 28, 13 + + + 16 + ECC 校檢 + + False + + + 81, 137 + + + 80, 20 + + + 15 + ECC 是設定啟用/關閉格雷的糾正碼。預設為關閉。如果啟用它你的資料會需要多一倍的字節發送,所以你會損失一半的頻寬,但是它最多能糾正每12位的數據中3位的錯誤。使用於遠距離時通常需配合較低的無線數據傳輸率。格雷解碼每個字節需要20微秒(每位元組則需要40微秒),也就是說在較高的傳輸速率時你的 CPU 會受到限制。所以速率最好不要超過 128kbps。 + + True + + + 3, 167 + + + 44, 13 + + + 18 + Mavlink 協議 + + True + + + 3, 194 + + + 61, 13 + + + 20 + 伺機重送 + + False + + + 81, 191 + + + 80, 20 + + + 19 + - OPPRESEND enables/disables "opportunistic resend". When enabled the radio will send a packet twice if the serial input buffer has less than 256 bytes in it. The 2nd send is marked as a resend and discarded by the receiving radio if it got the first packet OK. This makes a big difference to the link quality, especially for uplink commands. + OPPRESEND enables/disables "opportunistic resend". When enabled the radio will send a packet twice if the serial input buffer has less than 256 bytes in it. The 2nd send is marked as a resend and discarded by the receiving radio if it got the first packet OK. This makes a big difference to the link quality, especially for uplink commands. + + False + + + 83, 191 + + + 80, 20 + + + 29 + - OPPRESEND enables/disables "opportunistic resend". When enabled the radio will send a packet twice if the serial input buffer has less than 256 bytes in it. The 2nd send is marked as a resend and discarded by the receiving radio if it got the first packet OK. This makes a big difference to the link quality, especially for uplink commands. + OPPRESEND enables/disables "opportunistic resend". When enabled the radio will send a packet twice if the serial input buffer has less than 256 bytes in it. The 2nd send is marked as a resend and discarded by the receiving radio if it got the first packet OK. This makes a big difference to the link quality, especially for uplink commands. + + False + + + 83, 137 + + + 80, 20 + + + 27 + ECC 是設定啟用/關閉格雷的糾正碼。預設為關閉。如果啟用它你的資料會需要多一倍的字節發送,所以你會損失一半的頻寬,但是它最多能糾正每12位的數據中3位的錯誤。使用於遠距離時通常需配合較低的無線數據傳輸率。格雷解碼每個字節需要20微秒(每位元組則需要40微秒),也就是說在較高的傳輸速率時你的 CPU 會受到限制。所以速率最好不要超過 128kbps。 + + False + 1 @@ -371,10 +589,22 @@ 20 + + 83, 110 + + + 80, 21 + + + 26 + - 發射功率的單位為 dBm。 20dBm 就是 100mW。這個值的設定對於較低階的短距離測試非常有用。 + 發射功率的單位為 dBm。 20dBm 就是 100mW。這個值的設定對於較低階的短距離測試非常有用。 + + False + 1 @@ -465,9 +695,21 @@ 30 + + 83, 83 + + + 80, 21 + + + 25 + "網路 ID" 由 16 位元組成。這被用於發送跳頻順序並確保資料來自正確的無線電。請確保您和其他人是使用不同的"網路 ID" 除非你想要在同一個區域使用相同網域的無線電。 + + False + 250 @@ -507,9 +749,21 @@ 2 + + 83, 56 + + + 80, 21 + + + 24 + 速率是無線資料傳輸率單位為 kbps。128 表示 128kbps。 最大值為 192,最小值為 2。我不建議這個頻率低於 16 這樣會造成 tdm 同步時間太久。 + + False + 115 @@ -537,20 +791,44 @@ 1 + + 83, 29 + + + 80, 21 + + + 22 + - 鮑率的單位為 kbps. 所以 57 表示為 57600. + 鮑率的單位為 kbps. 所以 57 表示為 57600. + + 59, 38 + + + True + + + 250, 39 + + + 34 + - see the spec for a RSSI to dBm graph. The numbers at the end are: -txe: 發射端錯誤代碼 (eg. transmit timeouts) -rxe: 接收端錯誤代碼 (crc error, framing error etc) -stx: 發射端溢位錯誤代碼 -rrx: 接收端溢位錯誤代碼 -ecc: number of 12 bit words successfully corrected by the golay code -which result in a valid packet CRC + see the spec for a RSSI to dBm graph. The numbers at the end are: +txe: 發射端錯誤代碼 (eg. transmit timeouts) +rxe: 接收端錯誤代碼 (crc error, framing error etc) +stx: 發射端溢位錯誤代碼 +rrx: 接收端溢位錯誤代碼 +ecc: number of 12 bit words successfully corrected by the golay code +which result in a valid packet CRC + + False + 5 @@ -608,9 +886,21 @@ which result in a valid packet CRC 50 + + 96, 55 + + + 80, 21 + + + 42 + 跳頻的頻道數 + + False + 10 @@ -641,27 +931,63 @@ which result in a valid packet CRC 100 + + 96, 82 + + + 80, 21 + + + 43 + 允許傳送的時間百分比 + + False + 0 25 + + 96, 109 + + + 80, 21 + + + 44 + 先接收後傳送 + + False + 0 25 + + 97, 109 + + + 80, 21 + + + 56 + 先接收後傳送 + + False + 10 @@ -692,9 +1018,21 @@ which result in a valid packet CRC 100 + + 97, 82 + + + 80, 21 + + + 55 + 允許傳送的時間百分比 + + False + 5 @@ -752,9 +1090,21 @@ which result in a valid packet CRC 50 + + 97, 55 + + + 80, 21 + + + 54 + 跳頻的頻道數 + + False + 902000 @@ -782,9 +1132,21 @@ which result in a valid packet CRC 435000 + + 96, 28 + + + 80, 21 + + + 41 + 終止頻率的單位 kHz + + False + 902000 @@ -821,9 +1183,21 @@ which result in a valid packet CRC + + 96, 3 + + + 80, 21 + + + 46 + 起始頻率的單位 kHz + + False + 902000 @@ -851,9 +1225,21 @@ which result in a valid packet CRC 435000 + + 97, 3 + + + 80, 21 + + + 58 + 起始頻率的單位 kHz + + False + 902000 @@ -881,19 +1267,43 @@ which result in a valid packet CRC 435000 + + 97, 28 + + + 80, 21 + + + 53 + 終止頻率的單位 kHz + + True + + + 689, 11 + + + 63, 13 + + + 83 + Led 狀態說明 - 3DR 數傳有 2 個狀態指示燈, 一紅一綠。 -綠色 LED 閃爍 - 尋找另一個數傳中 -綠色 LED 恆亮 - 已連結另一個數傳 -紅色 LED 閃爍 - 資料傳輸中 + 3DR 數傳有 2 個狀態指示燈, 一紅一綠。 +綠色 LED 閃爍 - 尋找另一個數傳中 +綠色 LED 恆亮 - 已連結另一個數傳 +紅色 LED 閃爍 - 資料傳輸中 紅色 LED 恆亮 - 韌體更新中 + + False + 0 @@ -987,9 +1397,21 @@ which result in a valid packet CRC 30 + + 96, 164 + + + 80, 21 + + + 52 + 節點 ID。預設 ID 為 0。在多點作業的環境需要有一點為基礎點 。 + + False + 0 @@ -1083,124 +1505,800 @@ which result in a valid packet CRC 30 + + 97, 164 + + + 80, 21 + + + 64 + 節點 ID。預設 ID 為 0。在多點作業的環境需要有一點為基礎點 。 + + False + + + 81, 164 + + + 80, 21 + + + 21 + 速率是無線資料傳輸率單位為 kbps。128 表示 128kbps。 最大值為 192,最小值為 2。我不建議這個頻率低於 16 這樣會造成 tdm 同步時間太久。 + + False + + + 83, 163 + + + 80, 21 + + + 22 + 速率是無線資料傳輸率單位為 kbps。128 表示 128kbps。 最大值為 192,最小值為 2。我不建議這個頻率低於 16 這樣會造成 tdm 同步時間太久。 + + False + + + + NoControl + + + 96, 136 + + + 80, 20 + + + 56 + 開啟或關閉硬體流量控制 + + False + + + NoControl + + + 97, 136 + + + 80, 20 + + + 66 + 開啟或關閉硬體流量控制 + + 83, 3 + + + 80, 20 + + + 23 + + + 138, 12 + + + 102, 20 + + + 33 + + + 59, 12 + + + 147, 20 + + + 32 + + + True + + + 14, 15 + + + 42, 13 + + + 36 + 韌體版本 + + True + + + 18, 50 + + + 32, 13 + + + 37 + 信號強度 + + False + + + 237, 3 + + + 69, 39 + + + 21 + 儲存設定 + + 162, 3 + + + 69, 39 + + + 6 + 載入設定 + + 12, 361 + + + 310, 22 + + + 3 + + + 312, 3 + + + 121, 39 + + + 0 + 更新韌體 (地面端) + + True + + + NoControl + + + 4, 6 + + + 48, 13 + + + 47 + 跳頻 起始頻率 + + True + + + NoControl + + + 4, 31 + + + 51, 13 + + + 48 + 跳頻 終止頻率 + + True + + + NoControl + + + 4, 58 + + + 73, 13 + + + 49 + 跳頻 頻道數 + + True + + + NoControl + + + 4, 85 + + + 58, 13 + + + 50 + 允許發射時間% + + True + + + NoControl + + + 4, 112 + + + 50, 13 + + + 51 + 避開已用頻率 + + True + + + NoControl + + + 5, 112 + + + 50, 13 + + + 63 + 避開已用頻率 + + True + + + NoControl + + + 5, 85 + + + 58, 13 + + + 62 + 允許發射時間% + + True + + + NoControl + + + 5, 58 + + + 73, 13 + + + 61 + 跳頻 頻道數 + + True + + + NoControl + + + 5, 31 + + + 51, 13 + + + 60 + 跳頻 終止頻率 + + True + + + NoControl + + + 5, 6 + + + 48, 13 + + + 59 + 跳頻 起始頻率 + + True + + + NoControl + + + 6, 199 + + + 61, 13 + + + 72 + 伺機重送 + + True + + + NoControl + + + 6, 167 + + + 44, 13 + + + 71 + Mavlink 協議 + + True + + + NoControl + + + 6, 140 + + + 28, 13 + + + 70 + ECC 校檢 + + True + + + NoControl + + + 6, 113 + + + 52, 13 + + + 69 + 發射功率 + + True + + + NoControl + + + 6, 86 + + + 38, 13 + + + 68 + 網路 ID + + True + + + NoControl + + + 6, 59 + + + 53, 13 + + + 67 + 速率(空中) + + True + + + NoControl + + + 6, 6 + + + 39, 13 + + + 66 + 格式版本 + + True + + + NoControl + + + 6, 32 + + + 32, 13 + + + 65 + 鮑率 + + True + + + 4, 81 + + + True + + + NoControl + + + 3, 138 + + + 53, 13 + + + 57 + 流量管制 + + True + + + NoControl + + + 7, 189 + + + 146, 13 + + + 55 + 重設為標準 Mavlink 協議 + + True + + + 7, 202 + + + 124, 13 + + + 54 + 重設為最小延遲 + + True + + + NoControl + + + 4, 167 + + + 91, 13 + + + 53 + 最大窗口 (ms) + + 374, 221 + + + 172 + + + 1 + + + 0 + + + True + + + 4, 81 + + + True + + + NoControl + + + 4, 138 + + + 53, 13 + + + 67 + 流量管制 + + True + + + NoControl + + + 5, 167 + + + 91, 13 + + + 65 + 最大窗口 (ms) + + 357, 221 + + + 170 + + + 1 + + + 0 + + + False + + + NoControl + + + 346, 355 + + + 102, 29 + + + 78 + 複製設定至機上端 + + 207, 12 + + + 102, 20 + + + 79 + + + 12, 48 + + + 382, 306 + + + 80 + 地面端 + + 315, 12 + + + True + + + 61, 65 + + + 80 + + + 400, 48 + + + 367, 306 + + + 81 + 機上端 + + 246, 12 + + + True + + + 61, 65 + + + 81 + + + True + + + NoControl + + + 12, 15 + + + 42, 13 + + + 37 + 韌體版本 + + True + + + NoControl + + + 9, 11 + + + 0, 13 + + + 82 + + + NoControl + + + 439, 3 + + + 82, 39 + + + 84 + 還原出廠值 + + NoControl + + + 527, 3 + + + 96, 39 + + + 85 + 更新客制化韌體 + + True + + + 6, 13 + + + 781, 433 + \ No newline at end of file diff --git a/Radio/Sikradio.zh-TW.resx b/Radio/Sikradio.zh-TW.resx index 1fc40fb9c6..00fad60940 100644 --- a/Radio/Sikradio.zh-TW.resx +++ b/Radio/Sikradio.zh-TW.resx @@ -117,6 +117,23 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 12, 389 + + + 755, 36 + + + + 2 + + + 17, 17 + + + False + 115 @@ -144,18 +161,75 @@ 1 + + 81, 29 + + + 80, 21 + + + 4 + 鮑率的單位為 kbps. 所以 57 表示為 57600. + + True + + + 3, 32 + + + 32, 13 + + + 5 + 鮑率 + + 81, 3 + + + 80, 20 + + + 7 + + + True + + + 3, 6 + + + 39, 13 + + + 8 + 格式版本 + + True + + + 3, 59 + + + 53, 13 + + + 10 + 速率(空中) + + False + 250 @@ -195,12 +269,36 @@ 2 + + 81, 56 + + + 80, 21 + + + 9 + 速率是無線資料傳輸率單位為 kbps。128 表示 128kbps。 最大值為 192,最小值為 2。我不建議這個頻率低於 16 這樣會造成 tdm 同步時間太久。 + + True + + + 3, 86 + + + 38, 13 + + + 12 + 網路 ID + + False + 1 @@ -291,12 +389,36 @@ 30 + + 81, 83 + + + 80, 21 + + + 11 + "網路 ID" 由 16 位元組成。這被用於發送跳頻順序並確保資料來自正確的無線電。請確保您和其他人是使用不同的"網路 ID" 除非你想要在同一個區域使用相同網域的無線電。 + + True + + + 3, 113 + + + 52, 13 + + + 14 + 發射功率 + + False + 1 @@ -321,32 +443,128 @@ 20 + + 81, 110 + + + 80, 21 + + + 13 + 發射功率的單位為 dBm。 20dBm 就是 100mW。這個值的設定對於較低階的短距離測試非常有用。 + + True + + + 3, 138 + + + 28, 13 + + + 16 + ECC 校檢 + + False + + + 81, 137 + + + 80, 20 + + + 15 + ECC 是設定啟用/關閉格雷的糾正碼。預設為關閉。如果啟用它你的資料會需要多一倍的字節發送,所以你會損失一半的頻寬,但是它最多能糾正每12位的數據中3位的錯誤。使用於遠距離時通常需配合較低的無線數據傳輸率。格雷解碼每個字節需要20微秒(每位元組則需要40微秒),也就是說在較高的傳輸速率時你的 CPU 會受到限制。所以速率最好不要超過 128kbps。 + + True + + + 3, 167 + + + 44, 13 + + + 18 + Mavlink 協議 + + True + + + 3, 194 + + + 61, 13 + + + 20 + 伺機重送 + + False + + + 81, 191 + + + 80, 20 + + + 19 + - OPPRESEND enables/disables "opportunistic resend". When enabled the radio will send a packet twice if the serial input buffer has less than 256 bytes in it. The 2nd send is marked as a resend and discarded by the receiving radio if it got the first packet OK. This makes a big difference to the link quality, especially for uplink commands. + OPPRESEND enables/disables "opportunistic resend". When enabled the radio will send a packet twice if the serial input buffer has less than 256 bytes in it. The 2nd send is marked as a resend and discarded by the receiving radio if it got the first packet OK. This makes a big difference to the link quality, especially for uplink commands. + + False + + + 83, 191 + + + 80, 20 + + + 29 + - OPPRESEND enables/disables "opportunistic resend". When enabled the radio will send a packet twice if the serial input buffer has less than 256 bytes in it. The 2nd send is marked as a resend and discarded by the receiving radio if it got the first packet OK. This makes a big difference to the link quality, especially for uplink commands. + OPPRESEND enables/disables "opportunistic resend". When enabled the radio will send a packet twice if the serial input buffer has less than 256 bytes in it. The 2nd send is marked as a resend and discarded by the receiving radio if it got the first packet OK. This makes a big difference to the link quality, especially for uplink commands. + + False + + + 83, 137 + + + 80, 20 + + + 27 + ECC 是設定啟用/關閉格雷的糾正碼。預設為關閉。如果啟用它你的資料會需要多一倍的字節發送,所以你會損失一半的頻寬,但是它最多能糾正每12位的數據中3位的錯誤。使用於遠距離時通常需配合較低的無線數據傳輸率。格雷解碼每個字節需要20微秒(每位元組則需要40微秒),也就是說在較高的傳輸速率時你的 CPU 會受到限制。所以速率最好不要超過 128kbps。 + + False + 1 @@ -371,10 +589,22 @@ 20 + + 83, 110 + + + 80, 21 + + + 26 + - 發射功率的單位為 dBm。 20dBm 就是 100mW。這個值的設定對於較低階的短距離測試非常有用。 + 發射功率的單位為 dBm。 20dBm 就是 100mW。這個值的設定對於較低階的短距離測試非常有用。 + + False + 1 @@ -465,9 +695,21 @@ 30 + + 83, 83 + + + 80, 21 + + + 25 + "網路 ID" 由 16 位元組成。這被用於發送跳頻順序並確保資料來自正確的無線電。請確保您和其他人是使用不同的"網路 ID" 除非你想要在同一個區域使用相同網域的無線電。 + + False + 250 @@ -507,9 +749,21 @@ 2 + + 83, 56 + + + 80, 21 + + + 24 + 速率是無線資料傳輸率單位為 kbps。128 表示 128kbps。 最大值為 192,最小值為 2。我不建議這個頻率低於 16 這樣會造成 tdm 同步時間太久。 + + False + 115 @@ -537,20 +791,44 @@ 1 + + 83, 29 + + + 80, 21 + + + 22 + - 鮑率的單位為 kbps. 所以 57 表示為 57600. + 鮑率的單位為 kbps. 所以 57 表示為 57600. + + 59, 38 + + + True + + + 250, 39 + + + 34 + - see the spec for a RSSI to dBm graph. The numbers at the end are: -txe: 發射端錯誤代碼 (eg. transmit timeouts) -rxe: 接收端錯誤代碼 (crc error, framing error etc) -stx: 發射端溢位錯誤代碼 -rrx: 接收端溢位錯誤代碼 -ecc: number of 12 bit words successfully corrected by the golay code -which result in a valid packet CRC + see the spec for a RSSI to dBm graph. The numbers at the end are: +txe: 發射端錯誤代碼 (eg. transmit timeouts) +rxe: 接收端錯誤代碼 (crc error, framing error etc) +stx: 發射端溢位錯誤代碼 +rrx: 接收端溢位錯誤代碼 +ecc: number of 12 bit words successfully corrected by the golay code +which result in a valid packet CRC + + False + 5 @@ -608,9 +886,21 @@ which result in a valid packet CRC 50 + + 96, 55 + + + 80, 21 + + + 42 + 跳頻的頻道數 + + False + 10 @@ -641,27 +931,63 @@ which result in a valid packet CRC 100 + + 96, 82 + + + 80, 21 + + + 43 + 允許傳送的時間百分比 + + False + 0 25 + + 96, 109 + + + 80, 21 + + + 44 + 先接收後傳送 + + False + 0 25 + + 97, 109 + + + 80, 21 + + + 56 + 先接收後傳送 + + False + 10 @@ -692,9 +1018,21 @@ which result in a valid packet CRC 100 + + 97, 82 + + + 80, 21 + + + 55 + 允許傳送的時間百分比 + + False + 5 @@ -752,9 +1090,21 @@ which result in a valid packet CRC 50 + + 97, 55 + + + 80, 21 + + + 54 + 跳頻的頻道數 + + False + 902000 @@ -782,9 +1132,21 @@ which result in a valid packet CRC 435000 + + 96, 28 + + + 80, 21 + + + 41 + 終止頻率的單位 kHz + + False + 902000 @@ -821,9 +1183,21 @@ which result in a valid packet CRC + + 96, 3 + + + 80, 21 + + + 46 + 起始頻率的單位 kHz + + False + 902000 @@ -851,9 +1225,21 @@ which result in a valid packet CRC 435000 + + 97, 3 + + + 80, 21 + + + 58 + 起始頻率的單位 kHz + + False + 902000 @@ -881,19 +1267,43 @@ which result in a valid packet CRC 435000 + + 97, 28 + + + 80, 21 + + + 53 + 終止頻率的單位 kHz + + True + + + 689, 11 + + + 63, 13 + + + 83 + Led 狀態說明 - 3DR 數傳有 2 個狀態指示燈, 一紅一綠。 -綠色 LED 閃爍 - 尋找另一個數傳中 -綠色 LED 恆亮 - 已連結另一個數傳 -紅色 LED 閃爍 - 資料傳輸中 + 3DR 數傳有 2 個狀態指示燈, 一紅一綠。 +綠色 LED 閃爍 - 尋找另一個數傳中 +綠色 LED 恆亮 - 已連結另一個數傳 +紅色 LED 閃爍 - 資料傳輸中 紅色 LED 恆亮 - 韌體更新中 + + False + 0 @@ -987,9 +1397,21 @@ which result in a valid packet CRC 30 + + 96, 164 + + + 80, 21 + + + 52 + 節點 ID。預設 ID 為 0。在多點作業的環境需要有一點為基礎點 。 + + False + 0 @@ -1083,124 +1505,800 @@ which result in a valid packet CRC 30 + + 97, 164 + + + 80, 21 + + + 64 + 節點 ID。預設 ID 為 0。在多點作業的環境需要有一點為基礎點 。 + + False + + + 81, 164 + + + 80, 21 + + + 21 + 速率是無線資料傳輸率單位為 kbps。128 表示 128kbps。 最大值為 192,最小值為 2。我不建議這個頻率低於 16 這樣會造成 tdm 同步時間太久。 + + False + + + 83, 163 + + + 80, 21 + + + 22 + 速率是無線資料傳輸率單位為 kbps。128 表示 128kbps。 最大值為 192,最小值為 2。我不建議這個頻率低於 16 這樣會造成 tdm 同步時間太久。 + + False + + + + NoControl + + + 96, 136 + + + 80, 20 + + + 56 + 開啟或關閉硬體流量控制 + + False + + + NoControl + + + 97, 136 + + + 80, 20 + + + 66 + 開啟或關閉硬體流量控制 + + 83, 3 + + + 80, 20 + + + 23 + + + 138, 12 + + + 102, 20 + + + 33 + + + 59, 12 + + + 147, 20 + + + 32 + + + True + + + 14, 15 + + + 42, 13 + + + 36 + 韌體版本 + + True + + + 18, 50 + + + 32, 13 + + + 37 + 信號強度 + + False + + + 237, 3 + + + 69, 39 + + + 21 + 儲存設定 + + 162, 3 + + + 69, 39 + + + 6 + 載入設定 + + 12, 361 + + + 310, 22 + + + 3 + + + 312, 3 + + + 121, 39 + + + 0 + 更新韌體 (地面端) + + True + + + NoControl + + + 4, 6 + + + 48, 13 + + + 47 + 跳頻 起始頻率 + + True + + + NoControl + + + 4, 31 + + + 51, 13 + + + 48 + 跳頻 終止頻率 + + True + + + NoControl + + + 4, 58 + + + 73, 13 + + + 49 + 跳頻 頻道數 + + True + + + NoControl + + + 4, 85 + + + 58, 13 + + + 50 + 允許發射時間% + + True + + + NoControl + + + 4, 112 + + + 50, 13 + + + 51 + 避開已用頻率 + + True + + + NoControl + + + 5, 112 + + + 50, 13 + + + 63 + 避開已用頻率 + + True + + + NoControl + + + 5, 85 + + + 58, 13 + + + 62 + 允許發射時間% + + True + + + NoControl + + + 5, 58 + + + 73, 13 + + + 61 + 跳頻 頻道數 + + True + + + NoControl + + + 5, 31 + + + 51, 13 + + + 60 + 跳頻 終止頻率 + + True + + + NoControl + + + 5, 6 + + + 48, 13 + + + 59 + 跳頻 起始頻率 + + True + + + NoControl + + + 6, 199 + + + 61, 13 + + + 72 + 伺機重送 + + True + + + NoControl + + + 6, 167 + + + 44, 13 + + + 71 + Mavlink 協議 + + True + + + NoControl + + + 6, 140 + + + 28, 13 + + + 70 + ECC 校檢 + + True + + + NoControl + + + 6, 113 + + + 52, 13 + + + 69 + 發射功率 + + True + + + NoControl + + + 6, 86 + + + 38, 13 + + + 68 + 網路 ID + + True + + + NoControl + + + 6, 59 + + + 53, 13 + + + 67 + 速率(空中) + + True + + + NoControl + + + 6, 6 + + + 39, 13 + + + 66 + 格式版本 + + True + + + NoControl + + + 6, 32 + + + 32, 13 + + + 65 + 鮑率 + + True + + + 4, 81 + + + True + + + NoControl + + + 3, 138 + + + 53, 13 + + + 57 + 流量管制 + + True + + + NoControl + + + 7, 189 + + + 146, 13 + + + 55 + 重設為標準 Mavlink 協議 + + True + + + 7, 202 + + + 124, 13 + + + 54 + 重設為最小延遲 + + True + + + NoControl + + + 4, 167 + + + 91, 13 + + + 53 + 最大窗口 (ms) + + 374, 221 + + + 172 + + + 1 + + + 0 + + + True + + + 4, 81 + + + True + + + NoControl + + + 4, 138 + + + 53, 13 + + + 67 + 流量管制 + + True + + + NoControl + + + 5, 167 + + + 91, 13 + + + 65 + 最大窗口 (ms) + + 357, 221 + + + 170 + + + 1 + + + 0 + + + False + + + NoControl + + + 346, 355 + + + 102, 29 + + + 78 + 複製設定至機上端 + + 207, 12 + + + 102, 20 + + + 79 + + + 12, 48 + + + 382, 306 + + + 80 + 地面端 + + 315, 12 + + + True + + + 61, 65 + + + 80 + + + 400, 48 + + + 367, 306 + + + 81 + 機上端 + + 246, 12 + + + True + + + 61, 65 + + + 81 + + + True + + + NoControl + + + 12, 15 + + + 42, 13 + + + 37 + 韌體版本 + + True + + + NoControl + + + 9, 11 + + + 0, 13 + + + 82 + + + NoControl + + + 439, 3 + + + 82, 39 + + + 84 + 還原出廠值 + + NoControl + + + 527, 3 + + + 96, 39 + + + 85 + 更新客制化韌體 + + True + + + 6, 13 + + + 781, 433 + \ No newline at end of file diff --git a/Radio/Uploader.cs b/Radio/Uploader.cs index cb1609121c..e4395bf678 100644 --- a/Radio/Uploader.cs +++ b/Radio/Uploader.cs @@ -24,6 +24,9 @@ public enum Board : byte DEVICE_ID_RFD900U = 0X80 | 0x01, DEVICE_ID_RFD900P = 0x80 | 0x02, DEVICE_ID_RFD900X = 0x80 | 0x03, + DEVICE_ID_RFD900X2 = 0x80 | 0x04, + DEVICE_ID_RFD900UX2 = 0x80 | 0x05, + DEVICE_ID_RFD900UX = 0x80 | 0x08, FAILED = 0x11 diff --git a/Radio/XModem.cs b/Radio/XModem.cs index 027de004aa..90b81ea475 100644 --- a/Radio/XModem.cs +++ b/Radio/XModem.cs @@ -54,75 +54,135 @@ public static void SendBlock(FileStream fs, ICommsSerial Serial, int bNumber) var bytesRead = fs.Read(bits, 0, bits.Length); - if (bytesRead == bits.Length) - { - CRC = CRC_calc(bits, 128); - System.Buffer.BlockCopy(bits, 0, packet, 3, 128); - packet[131] = (byte)(CRC >> 8); - packet[132] = (byte)(CRC); - Serial.Write(packet, 0, packet.Length); - } - else if (bytesRead > 0) + CRC = CRC_calc(bits, 128); + System.Buffer.BlockCopy(bits, 0, packet, 3, 128); + packet[131] = (byte)(CRC >> 8); + packet[132] = (byte)(CRC); + Serial.Write(packet, 0, packet.Length); + } + + /// + /// + /// + /// Must be no more than 128 bytes in length. Must not be null. + /// + /// + public static void SendBlock(byte[] data, int DataLength, ICommsSerial Serial, int bNumber) + { + byte[] packet = new byte[133]; + byte[] bits = new byte[128]; + UInt16 CRC = 0; + + for (int i = 0; i < bits.Length; i++) { bits[i] = 0x26; } + + packet[0] = SOH; + packet[1] = (byte)(bNumber % 256); + packet[2] = (byte)(255 - (bNumber % 256)); + + Array.Copy(data, bits, DataLength); + + CRC = CRC_calc(bits, 128); + System.Buffer.BlockCopy(bits, 0, packet, 3, 128); + packet[131] = (byte)(CRC >> 8); + packet[132] = (byte)(CRC); + Serial.DiscardInBuffer(); + Serial.Write(packet, 0, packet.Length); + } + + static void SendEOT(ICommsSerial Serial) + { + Serial.Write(new byte[] { EOT }, 0, 1); + ProgressEvent?.Invoke(100); + } + + static bool UploadBlock(ICommsSerial comPort, byte[] Data, int DataLength, int bNumber) + { + for (int Retry = 0; Retry < 10; Retry++) { - CRC = CRC_calc(bits, 128); - System.Buffer.BlockCopy(bits, 0, packet, 3, 128); - packet[131] = (byte)(CRC >> 8); - packet[132] = (byte)(CRC); - Serial.Write(packet, 0, packet.Length); - Serial.Write(new byte[] { EOT }, 0, 1); - ProgressEvent?.Invoke(100); + //comPort.DiscardInBuffer(); + SendBlock(Data, DataLength, comPort, bNumber); + // responce ACK + var ack = comPort.ReadByte(); + while (ack == 'C') + { + ack = comPort.ReadByte(); + } + + if (ack == ACK) + { + return true; + } + + //Thread.Sleep(1000); } - else if (bytesRead == 0) + + return false; + } + + static bool UploadEnd(ICommsSerial comPort) + { + for (int Retry = 0; Retry < 10; Retry++) { - Serial.Write(new byte[] { EOT }, 0, 1); - ProgressEvent?.Invoke(100); + SendEOT(comPort); + + var ack = comPort.ReadByte(); + while (ack == 'C') + ack = comPort.ReadByte(); + + if (ack == ACK) + { + return true; + } + + //Thread.Sleep(1000); } + + /*MsgBox.CustomMessageBox.Show("Corrupted packet. Please power cycle and try again.\r\n", "Warning", + MessageBoxButtons.OK, MessageBoxIcon.Warning);*/ + return false; } - public static void Upload(string firmwarebin, ICommsSerial comPort) + public static bool Upload(string firmwarebin, ICommsSerial comPort) { comPort.ReadTimeout = 2000; - using (var fs = new FileStream(firmwarebin, FileMode.Open)) + using (var fs = new FileStream(firmwarebin, FileMode.OpenOrCreate,FileAccess.Read)) { var len = (int)fs.Length; len = (len % 128) == 0 ? len / 128 : (len / 128) + 1; var startlen = len; int a = 1; - int NoAckCount = 0; while (len > 0) { LogEvent?.Invoke("Uploading block " + a + "/" + startlen); - SendBlock(fs, comPort, a); - // responce ACK - var ack = comPort.ReadByte(); - while (ack == 'C') - ack = comPort.ReadByte(); + byte[] Data = new byte[128]; - if (ack == ACK) + var bytesRead = fs.Read(Data, 0, Data.Length); + + if (UploadBlock(comPort, Data, bytesRead, a)) { - NoAckCount = 0; len--; a++; - ProgressEvent?.Invoke(1 - ((double)len / (double)startlen)); } - else if (ack == NAK) - { - MsgBox.CustomMessageBox.Show("Corrupted packet. Please power cycle and try again.\r\n", "Warning", - MessageBoxButtons.OK, MessageBoxIcon.Warning); - len = 0; - } else { - NoAckCount++; - if (NoAckCount >= 10) - { - } + /*MsgBox.CustomMessageBox.Show("Corrupted packet. Please power cycle and try again.\r\n", "Warning", + MessageBoxButtons.OK, MessageBoxIcon.Warning);*/ + len = 0; + + return false; } } + + if (!UploadEnd(comPort)) + { + return false; + } + + //Console.WriteLine("Finished " + len.ToString()); } // boot @@ -131,6 +191,8 @@ public static void Upload(string firmwarebin, ICommsSerial comPort) Thread.Sleep(100); comPort.Write("BOOTNEW\r\n"); Thread.Sleep(100); + + return true; } } } diff --git a/SikRadio/ComPort.cs b/SikRadio/ComPort.cs new file mode 100644 index 0000000000..5dd3ce8ef1 --- /dev/null +++ b/SikRadio/ComPort.cs @@ -0,0 +1,18 @@ +using System; +using MissionPlanner.Comms; + +namespace MissionPlanner.Radio +{ + public static class ComPort + { + public static ICommsSerial GetComPortForSiKRadio() + { + return SikRadio.Config.comPort; + } + + public static void FinishedWithComPortForSiKRadio() + { + } + } + +} \ No newline at end of file diff --git a/SikRadio/Config.cs b/SikRadio/Config.cs index d11c1cf25d..898307b654 100644 --- a/SikRadio/Config.cs +++ b/SikRadio/Config.cs @@ -64,7 +64,7 @@ private ISikRadioForm loadSettings() var form = new Sikradio(); form.Enabled = false; - form.GetCommsSerialAlt = () => SikRadio.Config.comPort; + form.DoDisconnectReconnect += DoDisconnectReconnect; panel1.Controls.Add(form); @@ -113,6 +113,15 @@ private ISikRadioForm loadRssi() return form; } + void DoDisconnectReconnect() + { + if (_Connected) + { + Disconnect(); + Connect(); + } + } + private void CMB_SerialPort_SelectedIndexChanged(object sender, EventArgs e) { MainV2.comPort.BaseStream.PortName = CMB_SerialPort.Text; @@ -152,6 +161,7 @@ void ShowForm(Func Constructor) } _CurrentForm = Constructor(); _CurrentForm.Enabled = _Connected; + groupBox2.Text = _CurrentForm.Header; _CurrentForm.Show(); if (_Connected) { @@ -174,53 +184,9 @@ private void rssiToolStripMenuItem_Click(object sender, EventArgs e) ShowForm(loadRssi); } - void getTelemPortWithRadio(ref ICommsSerial comPort) - { - // try telem1 - - comPort = new MAVLinkSerialPort(MainV2.comPort, (int)MAVLink.SERIAL_CONTROL_DEV.TELEM1); - - comPort.ReadTimeout = 4000; - - comPort.Open(); - } - bool Connect() { - try - { - if (MainV2.comPort.BaseStream.PortName.Contains("TCP")) - { - _comPort = new TcpSerial(); - _comPort.BaudRate = MainV2.comPort.BaseStream.BaudRate; - _comPort.ReadTimeout = 4000; - _comPort.Open(); - } - else - { - _comPort = new SerialPort(); - - if (MainV2.comPort.BaseStream.IsOpen) - { - getTelemPortWithRadio(ref _comPort); - } - else - { - _comPort.PortName = MainV2.comPort.BaseStream.PortName; - _comPort.BaudRate = MainV2.comPort.BaseStream.BaudRate; - } - - _comPort.ReadTimeout = 4000; - - _comPort.Open(); - } - - return true; - } - catch - { - return false; - } + return Sikradio.Connect(ref _comPort); } bool Disconnect() diff --git a/SikRadio/Config.resx b/SikRadio/Config.resx index ee1684c823..16a68d4a7e 100644 --- a/SikRadio/Config.resx +++ b/SikRadio/Config.resx @@ -205,7 +205,7 @@ 781, 433 - 1127, 502 + 1127, 622 2 @@ -313,7 +313,7 @@ 12, 108 - 1132, 527 + 1132, 631 8 @@ -454,7 +454,7 @@ 42 - 1156, 639 + 1156, 743 diff --git a/SikRadio/ISikRadioForm.cs b/SikRadio/ISikRadioForm.cs index eeab6dc8ef..ec67d03385 100644 --- a/SikRadio/ISikRadioForm.cs +++ b/SikRadio/ISikRadioForm.cs @@ -8,5 +8,6 @@ public interface ISikRadioForm : IDisposable void Disconnect(); void Show(); bool Enabled { get; set; } + string Header { get; } } } \ No newline at end of file diff --git a/SikRadio/Program.cs b/SikRadio/Program.cs index 826e388b06..7e681fccca 100644 --- a/SikRadio/Program.cs +++ b/SikRadio/Program.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Windows.Forms; using log4net; using log4net.Config; @@ -8,6 +8,26 @@ namespace SikRadio internal static class Program { private static readonly ILog log = LogManager.GetLogger("Program"); + private const string MANUFACTURER_PASSWORD = "--manufacturing"; + private const string ALLOW_DIFF_PROG = "--allowdiffprog"; + private static bool _Manufacturer = false; + private static bool _AllowDiffProg = false; + + public static bool Manufacturer + { + get + { + return _Manufacturer; + } + } + + public static bool AllowDiffProg + { + get + { + return _AllowDiffProg; + } + } /// /// The main entry point for the application. @@ -15,6 +35,18 @@ internal static class Program [STAThread] private static void Main(string[] args) { + foreach (var arg in args) + { + if (RFDLib.Text.Contains(arg, MANUFACTURER_PASSWORD)) + { + _Manufacturer = true; + } + if (RFDLib.Text.Contains(arg, ALLOW_DIFF_PROG)) + { + _AllowDiffProg = true; + } + } + log.Info("App Start"); XmlConfigurator.Configure(); diff --git a/SikRadio/Properties/AssemblyInfo.cs b/SikRadio/Properties/AssemblyInfo.cs index 84f9190bfa..133ed2071e 100644 --- a/SikRadio/Properties/AssemblyInfo.cs +++ b/SikRadio/Properties/AssemblyInfo.cs @@ -36,4 +36,4 @@ // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("0.0.*")] -[assembly: AssemblyFileVersion("0.0.0.0")] \ No newline at end of file +[assembly: AssemblyFileVersion("0.0.0.0")] diff --git a/SikRadio/Properties/Resources.Designer.cs b/SikRadio/Properties/Resources.Designer.cs index 127522ddce..ebf5a332ba 100644 --- a/SikRadio/Properties/Resources.Designer.cs +++ b/SikRadio/Properties/Resources.Designer.cs @@ -60,6 +60,16 @@ internal Resources() { } } + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] RFDSiK_V3_00_rfd900x { + get { + object obj = ResourceManager.GetObject("RFDSiK_V3_00_rfd900x", resourceCulture); + return ((byte[])(obj)); + } + } + /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// diff --git a/SikRadio/Properties/Resources.resx b/SikRadio/Properties/Resources.resx index 780715af2f..81432cbd0c 100644 --- a/SikRadio/Properties/Resources.resx +++ b/SikRadio/Properties/Resources.resx @@ -118,6 +118,9 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + ..\Resources\RFDSiK V3.00 rfd900x.bin;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + ..\Resources\sik.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a diff --git a/SikRadio/RFD900.cs b/SikRadio/RFD900.cs index 14da57b4d5..18f82e16d1 100644 --- a/SikRadio/RFD900.cs +++ b/SikRadio/RFD900.cs @@ -2,6 +2,8 @@ using System.Collections.Generic; using System.Threading; using MissionPlanner.Radio; +using System.IO; +using System.Reflection; namespace RFD.RFD900 { @@ -13,11 +15,47 @@ public class TSession : IDisposable TMode _Mode = TMode.INIT; MissionPlanner.Comms.ICommsSerial _Port; public Uploader.Board Board = Uploader.Board.FAILED; + RFDLib.IO.ATCommand.TClient _ATCClient; + RFD900 _ModemObject; + int _MainFirmwareBaud; + /// + /// This is a work-around for radio firmware bugs in which not all settings + /// are described by an RTI5? command. + /// + Dictionary _DefaultSettings = new Dictionary(); + const int BOOTLOADERX_BAUD = 57600; const int BOOTLOADER_BAUD = 115200; + const string NODEID = "NODEID"; + const string NODEDESTINATION = "NODEDESTINATION"; - public TSession(MissionPlanner.Comms.ICommsSerial Port) + public TSession(MissionPlanner.Comms.ICommsSerial Port, int MainFirmwareBaud) { _Port = Port; + _ATCClient = new TATCClient(new TMissionPlannerSerialPort(Port)); + _ATCClient.Echoes = true; + _ATCClient.Terminator = "\r\n"; + _ATCClient.Timeout = 1000; + _MainFirmwareBaud = MainFirmwareBaud; + + // This is a work-around for radio firmware bugs in which not all settings + // are described by an RTI5? command. + AddDefaultSetting("S21:GPO1_3STATLED(N)[0..1]=0{Off,On,}\r\n"); + AddDefaultSetting("S22:GPO1_0TXEN485(N)[0..1]=0{Off,On,}\r\n"); + AddDefaultSetting("S23:RATE/FREQBAND(N)[0..3]=0{LoAus,HiAus,StdNZ,LoUSA,HiUSA,StdEU,LbtEU,StdPRC,StdINS,HiLbtJAP,HiStdJAP,LoLbtJAP,LoStdJAP,}\r\n"); + AddDefaultSetting("S20:ANT_MODE(N)[0..3]=0{Ant1&2,Ant1,Ant2,Ant1=TX;2=RX,}\r\n"); + } + + /// + /// Add a setting to the _DefaultSettings which is generated by parsing the given ATI5? query response line. + /// + /// The ATI5? query response line + void AddDefaultSetting(string ATI5QueryResponseLine) + { + var S = TSession.ParseATI5QueryResponseLine(ATI5QueryResponseLine); + if (S != null) + { + _DefaultSettings[S.Name] = S; + } } public enum TMode @@ -28,6 +66,11 @@ public enum TMode BOOTLOADER, //For a,u,+ BOOTLOADER_X, //For x UNKNOWN + } + + public void WriteToPort(string ToWrite) + { + _Port.Write(ToWrite); } public void Dispose() @@ -40,13 +83,13 @@ public bool WaitForToken(string Token, int MaxWait) return WaitForAnyOfTheseTokens(new string[] { Token }, MaxWait) >= 0; } - /// - /// Wait for any of these tokens to be received on the port. - /// Wait up to MaxWait milliseconds. If a token was received, return the array index of - /// the token. If no tokens received, return -1. - /// - /// The tokens to wait for. Must not be null. - /// The max wait time in milliseconds. + /// + /// Wait for any of these tokens to be received on the port. + /// Wait up to MaxWait milliseconds. If a token was received, return the array index of + /// the token. If no tokens received, return -1. + /// + /// The tokens to wait for. Must not be null. + /// The max wait time in milliseconds. /// The token array index of the token received, or -1 is none of the tokens received. public int WaitForAnyOfTheseTokens(string[] Tokens, int MaxWait) { @@ -58,20 +101,37 @@ public int WaitForAnyOfTheseTokens(string[] Tokens, int MaxWait) while (SW.ElapsedMilliseconds < MaxWait) { - string x = _Port.ReadExisting(); - Temp += x; - - for (int n = 0; n < Tokens.Length; n++) - { - if (Temp.Contains(Tokens[n])) - { - _Port.ReadTimeout = Timeout; - return n; + try + { + int Byte; + while (((Byte = _Port.ReadByte()) != -1) && (SW.ElapsedMilliseconds < MaxWait)) + { + //string x = _Port.ReadExisting(); + Temp += ((char)Byte); + + for (int n = 0; n < Tokens.Length; n++) + { + if (Temp.Contains(Tokens[n])) + { + _Port.ReadTimeout = Timeout; + return n; + } + } } + + Thread.Sleep(50); } + catch (Exception e) + { + //System.Diagnostics.Debug.WriteLine(e.Message); + //System.Diagnostics.Debug.WriteLine("No tokens, got " + Temp); - Thread.Sleep(50); + return -1; + } } + + //Console.WriteLine("Failed to get token, got this instead: " + Temp); + _Port.ReadTimeout = Timeout; return -1; } @@ -84,7 +144,6 @@ void WriteBootloaderCode(Uploader.Code Code) bool IsInBootloaderMode() { - int PrevBaud = _Port.BaudRate; _Port.BaudRate = BOOTLOADER_BAUD; Thread.Sleep(100); WriteBootloaderCode(Uploader.Code.EOC); @@ -103,7 +162,7 @@ bool IsInBootloaderMode() } if (!Result) { - _Port.BaudRate = PrevBaud; + _Port.BaudRate = _MainFirmwareBaud; } return Result; } @@ -114,6 +173,9 @@ bool IsInBootloaderMode() /// bool IsInBootloaderXMode() { + int PrevBaud = _Port.BaudRate; + _Port.BaudRate = BOOTLOADERX_BAUD; + //for (int n = 0; n < 3; n++) { Thread.Sleep(200); @@ -125,19 +187,16 @@ bool IsInBootloaderXMode() _Port.Write("CHIPID\r\n"); if (WaitForToken("RFD", 200)) { + //Leave baud rate at bootloader X baud rate. return true; } } + _Port.BaudRate = PrevBaud; return false; } - TMode DetermineMode() - { - if (IsInBootloaderXMode()) - { - return TMode.BOOTLOADER_X; - } - + bool TryEscapeFromTransparent() + { _Port.ReadTimeout = 2000; //Console.WriteLine("Waiting 1500ms"); Thread.Sleep(1500); @@ -145,10 +204,16 @@ TMode DetermineMode() //Console.WriteLine("Sending +++"); _Port.Write("+++"); //Console.WriteLine("Waiting up to 1.5s for OK"); - if (WaitForToken("OK\r\n", 1500)) + return WaitForToken("OK\r\n", 1500); + } + + TMode DetermineMode() + { + if (IsInBootloaderXMode()) { - return TMode.AT_COMMAND; + return TMode.BOOTLOADER_X; } + //Check if already in AT command mode. _Port.DiscardInBuffer(); _Port.Write("\r\n"); @@ -158,6 +223,12 @@ TMode DetermineMode() { return TMode.AT_COMMAND; } + + if (TryEscapeFromTransparent() || TryEscapeFromTransparent()) + { + return TMode.AT_COMMAND; + } + //Not in transparent or at command mode. Probably in bootloader mode. Need to verify. if (IsInBootloaderXMode()) { @@ -182,6 +253,14 @@ void CheckIfInBootloaderMode() } } + public RFDLib.IO.ATCommand.TClient ATCClient + { + get + { + return _ATCClient; + } + } + public MissionPlanner.Comms.ICommsSerial Port { get @@ -190,6 +269,79 @@ public MissionPlanner.Comms.ICommsSerial Port } } + /// + /// Get the modem object, assuming it can be put into AT command mode, or already is in AT command mode. + /// Returns null if could not get modem object. + /// + /// + public RFD900 GetModemObject() + { + if (_ModemObject == null) + { + switch (GetMode()) + { + case TMode.TRANSPARENT: + if (PutIntoATCommandMode() != TMode.AT_COMMAND) + { + return null; + } + goto case TMode.AT_COMMAND; + case TMode.AT_COMMAND: + string Result = _ATCClient.DoQuery("ATI2", true); + try + { + int Code = int.Parse(Result); + Uploader.Board Board = (Uploader.Board)Code; + switch (Board) + { + case Uploader.Board.DEVICE_ID_RFD900A: + _ModemObject = new RFD900a(this); + break; + case Uploader.Board.DEVICE_ID_RFD900P: + _ModemObject = new RFD900p(this); + break; + case Uploader.Board.DEVICE_ID_RFD900U: + _ModemObject = new RFD900u(this); + break; + case Uploader.Board.DEVICE_ID_RFD900UX: + _ModemObject = new RFD900ux(this); + break; + case Uploader.Board.DEVICE_ID_RFD900X: + _ModemObject = new RFD900x(this); + break; + case Uploader.Board.DEVICE_ID_RFD900X2: + _ModemObject = new RFD900X2(this); + break; + case Uploader.Board.DEVICE_ID_RFD900UX2: + _ModemObject = new RFD900UX2(this); + break; + } + } + catch + { + + } + + if (_ModemObject != null) + { + string ATIResponse = _ATCClient.DoQuery("ATI", true); + if (ATIResponse != null && ATIResponse.Contains("DINIO")) + { + _ModemObject.SetDINIO(); + } + } + break; + case TMode.BOOTLOADER: + _ModemObject = RFD900APU.GetObjectForModem(this); + break; + case TMode.BOOTLOADER_X: + _ModemObject = RFD900xux.GetObjectForModem(this); + break; + } + } + return _ModemObject; + } + public TMode GetMode() { if (_Mode == TMode.INIT) @@ -202,22 +354,22 @@ public TMode GetMode() public TMode PutIntoBootloaderMode() { var CurrentMode = GetMode(); - switch (CurrentMode) - { - case TMode.BOOTLOADER: - case TMode.BOOTLOADER_X: - break; - default: - CurrentMode = PutIntoATCommandMode(); - if (CurrentMode == TMode.AT_COMMAND) - { - _Port.Write("\r\n"); - Thread.Sleep(100); - _Port.Write("AT&UPDATE\r\n"); - Thread.Sleep(100); - CheckIfInBootloaderMode(); - } - break; + switch (CurrentMode) + { + case TMode.BOOTLOADER: + case TMode.BOOTLOADER_X: + break; + default: + CurrentMode = PutIntoATCommandMode(); + if (CurrentMode == TMode.AT_COMMAND) + { + _Port.Write("\r\n"); + Thread.Sleep(100); + _Port.Write("AT&UPDATE\r\n"); + Thread.Sleep(100); + CheckIfInBootloaderMode(); + } + break; } return GetMode(); @@ -231,10 +383,9 @@ public TMode PutIntoATCommandMode() case TMode.TRANSPARENT: return PutIntoATCommandModeAssumingInTransparentMode(); case TMode.BOOTLOADER: - int PrevBaud = _Port.BaudRate; _Port.BaudRate = BOOTLOADER_BAUD; WriteBootloaderCode(Uploader.Code.REBOOT); - _Port.BaudRate = PrevBaud; + _Port.BaudRate = _MainFirmwareBaud; _Mode = TMode.INIT; break; case TMode.BOOTLOADER_X: @@ -242,102 +393,130 @@ public TMode PutIntoATCommandMode() Thread.Sleep(100); _Port.Write("\r\n"); Thread.Sleep(100); - _Port.Write("BOOTNEW\r\n"); + _Port.Write("RESET\r\n"); Thread.Sleep(100); + _Port.BaudRate = _MainFirmwareBaud; _Mode = TMode.INIT; break; } return GetMode(); } - /// - /// Assuming in transparent mode, try to put it into AT command mode. - /// If fails, determine mode and then try again. + /// + /// Assuming in transparent mode, try to put it into AT command mode. + /// If fails, determine mode and then try again. /// - public TMode PutIntoATCommandModeAssumingInTransparentMode() - { + public TMode PutIntoATCommandModeAssumingInTransparentMode() + { _Port.ReadTimeout = 2000; //Console.WriteLine("Waiting 1500ms"); Thread.Sleep(1500); _Port.DiscardInBuffer(); //Console.WriteLine("Sending +++"); - _Port.Write("+++"); + _Port.Write("+++"); //Console.WriteLine("Waiting up to 3s for OK"); if (WaitForToken("OK\r\n", 3000)) { _Mode = TMode.AT_COMMAND; - return TMode.AT_COMMAND; - } - - _Mode = TMode.INIT; - return PutIntoATCommandMode(); + return TMode.AT_COMMAND; + } + + _Mode = TMode.INIT; + return PutIntoATCommandMode(); + } + + /// + /// Put into transparent mode. + /// + /// + public TMode PutIntoTransparentMode() + { + if (GetMode() == TMode.TRANSPARENT) + { + System.Diagnostics.Debug.WriteLine("Already in transparent mode"); + return TMode.TRANSPARENT; + } + else + { + if (PutIntoATCommandMode() == TMode.AT_COMMAND) + { + _Port.Write("\r\n"); + Thread.Sleep(100); + _Port.Write("ATO\r\n"); + Thread.Sleep(100); + if (WaitForToken("ATO\r\n", 100)) + { + System.Diagnostics.Debug.WriteLine("Put into transparent mode OK"); + } + else + { + System.Diagnostics.Debug.WriteLine("Failed to put into transparent mode"); + } + _Mode = TMode.TRANSPARENT; + return TMode.TRANSPARENT; + } + else + { + System.Diagnostics.Debug.WriteLine("Failed to put into AT cmd mode"); + return TMode.UNKNOWN; + } + } + } + + public void AssumeMode(TMode Mode) + { + if (Mode == TMode.BOOTLOADER_X) + { + _Port.BaudRate = BOOTLOADERX_BAUD; + } + else + { + _Port.BaudRate = _MainFirmwareBaud; + } + _Mode = Mode; } /// - /// Put into transparent mode. + /// Skip over the multipoint node number in an ATI5 or ATI5? response line. /// - /// - public TMode PutIntoTransparentMode() + /// The response line + /// The character index of the designator. + static int SkipMultipointNodeNumber(string Line) { - if (GetMode() == TMode.TRANSPARENT) - { - System.Diagnostics.Debug.WriteLine("Already in transparent mode"); - return TMode.TRANSPARENT; - } - else + int n = 0; + + for (; n < Line.Length; n++) { - if (PutIntoATCommandMode() == TMode.AT_COMMAND) - { - _Port.Write("\r\n"); - Thread.Sleep(100); - _Port.Write("ATO\r\n"); - Thread.Sleep(100); - if (WaitForToken("ATO\r\n", 100)) - { - System.Diagnostics.Debug.WriteLine("Put into transparent mode OK"); - } - else - { - System.Diagnostics.Debug.WriteLine("Failed to put into transparent mode"); - } - _Mode = TMode.TRANSPARENT; - return TMode.TRANSPARENT; - } - else + if (RFDLib.Text.CheckIsLetter(Line[n])) { - System.Diagnostics.Debug.WriteLine("Failed to put into AT cmd mode"); - return TMode.UNKNOWN; + return n; } } - } - - public void AssumeMode(TMode Mode) - { - _Mode = Mode; - } + return 0; + } + /// /// Parse the setting designator from the given Line and character positon. /// /// The ATI5? line. Must not be null. /// The current character position. /// - string ParseDesignator(string Line, ref int n) + static string ParseDesignator(string Line, ref int n) { - n = 0; + n = SkipMultipointNodeNumber(Line); string Designator = ""; - - if (RFDLib.Text.CheckIsLetter(Line[0])) + + if (RFDLib.Text.CheckIsLetter(Line[n])) { - Designator += Line[0]; + Designator += Line[n]; + n++; } else { return null; } - n = 1; - while (Line[n] != ':') { if (RFDLib.Text.CheckIsNumeral(Line[n])) @@ -354,7 +533,7 @@ string ParseDesignator(string Line, ref int n) return Designator; } - string ParseName(string Line, ref int n) + static string ParseName(string Line, ref int n) { string Name = ""; while (true) @@ -371,7 +550,7 @@ string ParseName(string Line, ref int n) } } - bool ParseType(string Line, ref int n) + static bool ParseType(string Line, ref int n) { for (; Line[n] != ')'; n++) { @@ -380,34 +559,60 @@ bool ParseType(string Line, ref int n) return true; } - bool ParseIntUntil(string Line, ref int n, string Delimiter, out int Value) + /// + /// Parse an integer from the given string, starting at the given character index until a delimiter + /// is reached. + /// + /// The string to parse from. Must not be null. + /// The start character index, to be updated to be at the end of the value + /// and/or delimiter. + /// The delimiter to end at. Can be null if not applicable. + /// The value parsed. + /// true if parsed, otherwise false. + static bool ParseIntUntil(string Line, ref int n, string Delimiter, out int Value) { string Temp = ""; + //Copy all the characters which are decimal numerals to Temp, incrementing + //n until it is at a non-numeral character. while ((n < Line.Length) && RFDLib.Text.CheckIsNumeral(Line[n])) { Temp += Line[n]; n++; } + //Parse Temp. Value = int.Parse(Temp); + //If no delimiter specified... if (Delimiter == null) { + //Just return now. return true; } else { + //If the delimiter exists in the string and it's at the current character index... if (Line.IndexOf(Delimiter, n) == n) { + //Move the character index to character after the end of it. n += Delimiter.Length; return true; } else { + //The delimiter doesn't exist. Return that failed. return false; } } } - TSetting.TRange ParseRange(string Line, ref int n) + /// + /// Parse a [min..max] range + /// + /// The ATI5? response line to parse from. Must not be null. + /// The index of the character to start parsing from. This is also updated to be + /// just after the end of the range. + /// The parameter name. Must not be null. + /// The range if successfully parsed, otherwise null. + static TSetting.TRange ParseRange(string Line, ref int n, string Name) { if (Line[n] != '[') { @@ -424,15 +629,15 @@ TSetting.TRange ParseRange(string Line, ref int n) { return null; } - return new TSetting.TRange(Min, Max); + return new TSetting.TSimpleRange(Min, Max, GetIncrement(Name)); } - bool ParseValue(string Line, ref int n, out int Value) + static bool ParseValue(string Line, ref int n, out int Value) { return ParseIntUntil(Line, ref n, null, out Value); } - bool CharArrayContains(char[] Array, char x) + static bool CharArrayContains(char[] Array, char x) { foreach (var c in Array) { @@ -444,7 +649,7 @@ bool CharArrayContains(char[] Array, char x) return false; } - string GetStringUntil(string Line, ref int n, char[] Tokens) + static string GetStringUntil(string Line, ref int n, char[] Tokens) { string Result = ""; while (!CharArrayContains(Tokens, Line[n])) @@ -455,7 +660,7 @@ string GetStringUntil(string Line, ref int n, char[] Tokens) return Result; } - string[] ParseOptions(string Line, ref int n) + static string[] ParseOptions(string Line, ref int n) { if (Line[n] != '{') { @@ -483,7 +688,13 @@ string[] ParseOptions(string Line, ref int n) return Options.ToArray(); } - int[] CheckIfAllIntegers(string[] Options) + /// + /// Try to parse every element in the given array of strings to integers. + /// If successful, return the corresponding array of integers, otherwise return null. + /// + /// The array of strings to convert. Must not be null. + /// The array of integers, or null if unsuccessful. + static int[] CheckIfAllIntegers(string[] Options) { int[] Result = new int[Options.Length]; @@ -497,18 +708,84 @@ int[] CheckIfAllIntegers(string[] Options) return Result; } - float GetScaleFactor(string Name) + static float GetScaleFactor(string Name, int[] Ints, int Value) { switch (Name) { case "SERIAL_SPEED": + foreach (var i in Ints) + { + if (i < 1000) + { + return 1; + } + } return 0.001F; + default: + if (Value != 0) + { + //Check if value matches one of the options divided by 1000. + foreach (var i in Ints) + { + if (i == Value) + { + return 1; + } + } + foreach (var i in Ints) + { + if ((i / 1000) == Value) + { + return 0.001F; + } + } + } + break; } return 1; } - TSetting.TOption[] CreateOptions(TSetting.TRange Range, string[] Options, - string Name) + static bool IsCapitalLetter(char Letter) + { + return (Letter >= 'A') && (Letter <= 'Z'); + } + + /* + * A new word starts when it goes from a lower case to upper case, + * or the character before the transition from upper case to lower. + */ + static string[] SplitOptionName(string OptionName) + { + int LastStart = 0; + List Result = new List(); + + for (int n = 1; n < OptionName.Length; n++) + { + if (IsCapitalLetter(OptionName[n]) && !IsCapitalLetter(OptionName[n-1])) + { + Result.Add(OptionName.Substring(LastStart, n - LastStart)); + LastStart = n; + } + else if (IsCapitalLetter(OptionName[n-1]) && !IsCapitalLetter(OptionName[n])) + { + if (LastStart != (n-1)) + { + Result.Add(OptionName.Substring(LastStart, n - LastStart-1)); + LastStart = n-1; + } + } + } + + if (LastStart < OptionName.Length) + { + Result.Add(OptionName.Substring(LastStart, OptionName.Length - LastStart)); + } + + return Result.ToArray(); + } + + static TSetting.TOption[] CreateOptions(TSetting.TRange Range, string[] Options, + string Name, int Value) { if (Range == null || Options == null) { @@ -516,10 +793,11 @@ TSetting.TOption[] CreateOptions(TSetting.TRange Range, string[] Options, } int[] Ints = CheckIfAllIntegers(Options); + //If all of the strings were integers... if (Ints != null) { TSetting.TOption[] Result = new TSetting.TOption[Ints.Length]; - float SF = GetScaleFactor(Name); + float SF = GetScaleFactor(Name, Ints, Value); for (int n = 0; n < Result.Length; n++) { Result[n] = new TSetting.TOption((int)(SF * Ints[n]), Options[n]); @@ -527,26 +805,95 @@ TSetting.TOption[] CreateOptions(TSetting.TRange Range, string[] Options, return Result; } - //Not all integers. - if (Range == null) - { - return null; - } + int[] RO = Range.GetOptionsIncludingValue(Value); if (Options.Length == 2) { //Match up with min and max. TSetting.TOption[] Result = new TSetting.TOption[2]; - Result[0] = new TSetting.TOption(Range.Min, Options[0]); - Result[1] = new TSetting.TOption(Range.Max, Options[1]); + + Result[0] = new TSetting.TOption(RO[0], Options[0]); + Result[1] = new TSetting.TOption(RO[RO.Length-1], Options[1]); return Result; } + else if (RO.Length == Options.Length) + { + TSetting.TOption[] Result = new TSetting.TOption[RO.Length]; + for (int n = 0; n < (RO.Length); n++) + { + Result[n] = new TSetting.TOption(RO[n], Options[n]); + } + return Result; + } + else if (RO.Length < Options.Length) + { + //Account for the freq/band setting. + string[] ResultOptionStrings = new string[RO.Length]; + string CountryCode = null; + int OptionIndex = 0; + bool GotToMax = false; + + //For each option... + foreach (var O in Options) + { + string[] Temp = SplitOptionName(O); + if (Temp.Length != 0) + { + //Extract country code from option name + string CCTemp = Temp[Temp.Length - 1]; + //If we're still on the same contry code... + if (CCTemp == CountryCode) + { + OptionIndex++; + if (OptionIndex >= (ResultOptionStrings.Length - 1)) + { + GotToMax = true; + } + } + else + { + OptionIndex = 0; + CountryCode = CCTemp; + } + + if (OptionIndex < ResultOptionStrings.Length) + { + if (ResultOptionStrings[OptionIndex] == null) + { + ResultOptionStrings[OptionIndex] = ""; + } + if (OptionIndex < ResultOptionStrings.Length) + { + if (ResultOptionStrings[OptionIndex].Length != 0) + { + ResultOptionStrings[OptionIndex] += "/"; + } + ResultOptionStrings[OptionIndex] += O; + } + } + } + } + + if (GotToMax) + { + TSetting.TOption[] Result = new TSetting.TOption[RO.Length]; + for (int n = 0; n < RO.Length; n++) + { + Result[n] = new TSetting.TOption(RO[n], ResultOptionStrings[n]); + } + return Result; + } + else + { + return null; + } + } else { return null; } } - int GetIncrement(string Name) + static int GetIncrement(string Name) { switch (Name) { @@ -569,7 +916,7 @@ int GetIncrement(string Name) /// /// The line. Must not be null. /// The setting parsed, or null if could not parse setting. - TSetting ParseATI5QueryResponseLine(string Line) + public static TSetting ParseATI5QueryResponseLine(string Line) { try { @@ -585,7 +932,9 @@ TSetting ParseATI5QueryResponseLine(string Line) n++; string Name = ParseName(Line, ref n); - if (Name == null || Name.Length == 0) + + //Ignore "reserved" setting (it's not a setting, just a reserved place for a setting). + if (Name == null || Name.Length == 0 || Name == "RESERVED") { return null; } @@ -596,7 +945,7 @@ TSetting ParseATI5QueryResponseLine(string Line) { return null; } - Range = ParseRange(Line, ref n); + Range = ParseRange(Line, ref n, Name); if (Range == null) { return null; @@ -631,7 +980,7 @@ TSetting ParseATI5QueryResponseLine(string Line) } return new TSetting(Designator, Name, Range, Value, - CreateOptions(Range, Options, Name), GetIncrement(Name)); + CreateOptions(Range, Options, Name, Value), GetIncrement(Name)); } catch { @@ -639,6 +988,47 @@ TSetting ParseATI5QueryResponseLine(string Line) } } + /// + /// Parse an ATI5 response line to a TShortSetting. + /// + /// + /// + TShortSetting ParseATI5ResponseLine(string Line) + { + try + { + int n = 0; + string Designator = ParseDesignator(Line, ref n); + if (Designator == null) + { + return null; + } + + n++; + string Name = ParseName(Line, ref n); + if (Name == null || Name.Length == 0) + { + return null; + } + if (Line[n] != '=') + { + return null; + } + n++; + + int Value; + if (!ParseValue(Line, ref n, out Value)) + { + return null; + } + return new TShortSetting(Designator, Name, Value); + } + catch + { + return null; + } + } + TSetting.TOption[] GetBaudRateOptionsGivenRawBaudRates(int[] Rates) { List Options = new List(); @@ -668,8 +1058,8 @@ TSetting.TOption[] GetDefaultBaudRateSettingForBoard(Uploader.Board Board) /// /// The full ATI5 query response. Must not be null. /// A dictionary of settings found, with name as key. Never null. - Dictionary ParseATI5QueryResponse(string ATI5Response, - Dictionary Dict) + Dictionary ParseATI5QueryResponse(string ATI5Response, + Dictionary Dict) { foreach (var Line in ATI5Response.Split('\n', '\r')) { @@ -683,32 +1073,456 @@ Dictionary ParseATI5QueryResponse(string ATI5Response, return Dict; } - public Dictionary GetSettings(string ATI5Response, - Uploader.Board Board) + Dictionary ParseATI5Response(string ATI5Response, + Dictionary Dict) { - Dictionary Result = new Dictionary(); - ParseATI5QueryResponse(ATI5Response, Result); - string SerialName = "SERIAL_SPEED"; - if (Result.ContainsKey(SerialName)) + foreach (var Line in ATI5Response.Split('\n', '\r')) { - if (Result[SerialName].Options == null) + var S = ParseATI5ResponseLine(Line); + if (S != null) { - Result[SerialName].Options = GetDefaultBaudRateSettingForBoard(Board); + Dict[S.Name] = S; } } - return Result; + + return Dict; } - } - public class TSetting - { + TTextSetting GetEncryptionKey(bool Remote) + { + string Temp = _ATCClient.DoQuery((Remote ? "R" : "A") + "T&E?", true); + + if (!Temp.Contains("OK") && !Temp.Contains("ERROR")) + { + foreach (char c in Temp) + { + if (!RFDLib.Text.CheckIsHexNumeral(c)) + { + return null; + } + } + + var Result = new TTextSetting(); + Result.Designator = "&E"; + Result.Name = ENC_KEY_SETTING_NAME; + Result.Text = Temp; + + return Result; + } + + return null; + } + + /// + /// Parse the ATI5? and ATI5 responses to get a set of settings objects. Settings are only returned if they're + /// in the ATI5QResponse, or Ranges. + /// + /// The ATI5? response + /// The board + /// The ATI5 response + /// A previous set of settings received to use the ranges from, in the + /// case that they can't be ascertained from ATI5QResponse. Can be null if not specified. + /// Never null + public Dictionary GetSettings(string ATI5QResponse, + Uploader.Board Board, string ATI5Response, + Dictionary Ranges, out bool UseRanges) + { + UseRanges = false; + + Dictionary Result = new Dictionary(); + ParseATI5QueryResponse(ATI5QResponse, Result); + + if (Result.Count == 0) + { + UseRanges = true; + } + else + { + string SerialName = "SERIAL_SPEED"; + if (Result.ContainsKey(SerialName) && Result[SerialName] is TSetting) + { + if (((TSetting)Result[SerialName]).Options == null) + { + ((TSetting)Result[SerialName]).Options = GetDefaultBaudRateSettingForBoard(Board); + } + } + } + + //The code below is all done to work-around the case of SiK RFD900x 3.07 bug in + //which are RTI5? commands response is cut-off for settings S21 through S23. + Dictionary ShortSettings = new Dictionary(); + ParseATI5Response(ATI5Response, ShortSettings); + + if (UseRanges && (Ranges != null)) + { + Result = CopySettingsButUseDifferentRanges(ShortSettings, Ranges); + } + + foreach (var kvp in _DefaultSettings) + { + if (ShortSettings.ContainsKey(kvp.Key) && !Result.ContainsKey(kvp.Key)) + { + kvp.Value.Value = ShortSettings[kvp.Key].Value; + Result[kvp.Key] = kvp.Value; + } + } + + //If the result contains a nodeid and nodedestination parameter, it's multipoint firmware + //and it needs its settings ranges modified... + //We want the node dest to have the same options as node id, except we want the + //last value parsed for node dest to be appended to the end for the node dest. + if (Result.ContainsKey(NODEID) && Result.ContainsKey(NODEDESTINATION) && + Result[NODEID] is TSetting && Result[NODEDESTINATION] is TSetting) + { + var NodeIDRange = ((TSetting)Result[NODEID]).Range; + var NodeDestRange = ((TSetting)Result[NODEDESTINATION]).Range; + //If the destination id range is null... + if (NodeDestRange == null) + { + //Do nothing + } + else if (NodeIDRange != null) + { + //Both parameters have parsed ranges... + if (NodeIDRange is TSetting.TSimpleRange) + { + //The node id range is a simple range. + var NodeIDOptions = NodeIDRange.GetOptionsIncludingValue(((TSetting)Result[NODEID]).Value); + var NodeDestOptions = NodeDestRange.GetOptionsIncludingValue(((TSetting)Result[NODEDESTINATION]).Value); + if (NodeDestOptions[NodeDestOptions.Length-1] > NodeIDOptions[NodeIDOptions.Length-1]) + { + ((TSetting)Result[NODEDESTINATION]).Range = new TSetting.TMultiRange(new TSetting.TSimpleRange[] + { + (TSetting.TSimpleRange)NodeIDRange, + new TSetting.TSimpleRange(NodeDestOptions[NodeDestOptions.Length-1], NodeDestOptions[NodeDestOptions.Length-1], 1) + } + ); + } + } + } + } + + return Result; + } + + /// + /// Copy the given dictionary of Values, but use the specified valid ranges/options + /// in the given Settings dictionary if they exist in there. + /// + /// This is useful for the case + /// of a local modem returning a full "ATI5?" reply, but the remote modem only returning + /// an "RTI5" reply. In this case, the values returned from RTI5 can be used, but the + /// ATI5? reply is used to assume the valid ranges of the settings of the remote modem. + /// + /// The values to use in the result. + /// The ranges/options to use in the result. + /// The resulting settings which contains the same values as Values, but has ranges/options + /// as specified in Settings (if they exist in there) + public Dictionary CopySettingsButUseDifferentRanges( + Dictionary Values, + Dictionary Settings) + { + Dictionary Result = new Dictionary(); + + foreach (var kvp in Values) + { + if (Settings.ContainsKey(kvp.Key) && Settings[kvp.Key] is TSetting) + { + var S = (TSetting)Settings[kvp.Key].Clone(); + S.Value = kvp.Value.Value; + Result[kvp.Key] = S; + } + } + + return Result; + } + + /// + /// Use the newer ATI10 command to assemble the same format string as the old ATI5? command. + /// ATI5? command has been deprecated, and ATI10 is used in its place. Returns the complete string, + /// or null if unsuccessful. + /// + /// The complete string, or null if unsuccessful. + public string UseATI10ToGetATI5QueryResponse(bool Remote) + { + int ParamIndex; + string Result = ""; + string Prefix = Remote ? "RTI10:" : "ATI10:"; + + for (ParamIndex = 0; ; ParamIndex++) + { + string Line = ATCClient.DoQuery(Prefix + ParamIndex.ToString(), true); + if (RFDLib.Text.Contains(Line, "error") || (Line.Length == 0)) + { + return null; + } + else if (RFDLib.Text.Contains(Line, "eof") || !Line.Contains("=")) + { + return Result; + } + else + { + Result += Line + "\r\n"; + } + } + } + + /// + /// Query the remote modem for its settings valid ranges and parse the given RTI5 response to get a set of settings objects. + /// + /// The board + /// The RTI5 response + /// A previous set of settings received to use the ranges from, in the + /// case that they can't be ascertained from querying the modem. Can be null if not specified. + /// Never null + public Dictionary GetSettings(bool Remote, Uploader.Board Board, string ATI5Response, + Dictionary Ranges, out bool UseRanges) + { + string ATI5QR = UseATI10ToGetATI5QueryResponse(Remote); + if (ATI5QR == null || (ATI5QR.Length == 0)) + { + ATI5QR = ATCClient.DoQueryWithMultiLineResponse(Remote ? "RTI5?" : "ATI5?"); + } + + var Result = GetSettings(ATI5QR, Board, ATI5Response, Ranges, out UseRanges); + + var EncKey = GetEncryptionKey(Remote); + + if (EncKey != null) + { + Result[ENC_KEY_SETTING_NAME] = EncKey; + } + + return Result; + } + + const string ENC_KEY_SETTING_NAME = "AESKEY"; + } + + public class TSettings + { + Dictionary _Settings = new Dictionary(); + const string MIN_FREQ = "MIN_FREQ"; + const string MAX_FREQ = "MAX_FREQ"; + + public TSettings(Dictionary Settings) + { + _Settings = Settings; + } + + public Dictionary Settings + { + get + { + return _Settings; + } + } + + public TSettings Clone() + { + Dictionary Temp = new Dictionary(); + + foreach (var kvp in _Settings) + { + Temp[kvp.Key] = (TBaseSetting)kvp.Value.Clone(); + } + + return new TSettings(Temp); + } + + public bool SaveToFile(string Path) + { + try + { + using (StreamWriter Writer = File.CreateText(Path)) + { + //Save in alphabetical order... + List Names = new List(_Settings.Keys); + Names.Sort(); + + foreach (var N in Names) + { + Writer.WriteLine(N + " = " + _Settings[N].GetValueAsString()); + } + } + + return true; + } + catch + { + return false; + } + } + + TNameAndValue ParseINILine(string Line) + { + if (!Line.Trim().StartsWith(";") && !Line.Trim().StartsWith("#")) + { + string[] ContentAndComment = Line.Split(';', '#'); + + if (ContentAndComment.Length >= 1) + { + string Content = ContentAndComment[0]; + + if (Content.Contains("=")) + { + string[] NameAndValue = Content.Split('='); + + if (NameAndValue.Length == 2) + { + string Name = NameAndValue[0].Trim(); + + return new TNameAndValue(Name, NameAndValue[1].Trim()); + } + } + } + } + + return null; + } + + /// + /// Update these settings from a file. + /// + /// The path of the settings file. Must not be null. + /// The names and values loaded. Null if failed. + public Dictionary LoadFromFile(string Path) + { + Dictionary Result = new Dictionary(); + + try + { + using (StreamReader Reader = File.OpenText(Path)) + { + string Line; + + while ((Line = Reader.ReadLine()) != null) + { + var NV = ParseINILine(Line); + if (NV != null && _Settings.ContainsKey(NV.Name)) + { + _Settings[NV.Name].SetValueFromString(NV.Value); + Result[NV.Name] = NV; + } + } + } + + return Result; + } + catch + { + return null; + } + } + + /// + /// Check if this set of settings is valid. Return a list of error descriptions. + /// If returned array is zero-length, settings are valid. + /// + /// + public string[] CheckValid() + { + List Result = new List(); + + if (_Settings.ContainsKey(MIN_FREQ) && _Settings.ContainsKey(MAX_FREQ)) + { + var Min = _Settings[MIN_FREQ]; + var Max = _Settings[MAX_FREQ]; + + if (Min is TShortSetting && Max is TShortSetting) + { + if (((TShortSetting)Min).Value > ((TShortSetting)Max).Value) + { + Result.Add("MIN_FREQ can't be more than MAX_FREQ"); + } + } + } + + return Result.ToArray(); + } + + public class TNameAndValue + { + public readonly string Name; + public readonly string Value; + + public TNameAndValue(string Name, string Value) + { + this.Name = Name; + this.Value = Value; + } + } + } + + public abstract class TBaseSetting : ICloneable + { public string Designator; public string Name; + + public abstract string GetValueAsString(); + public abstract void SetValueFromString(string Text); + public abstract object Clone(); + } + + public class TTextSetting : TBaseSetting + { + public string Text; + + public override string GetValueAsString() + { + return Text; + } + + public override void SetValueFromString(string Text) + { + this.Text = Text; + } + + public override object Clone() + { + TTextSetting Result = new TTextSetting(); + Result.Designator = Designator; + Result.Name = Name; + Result.Text = Text; + + return Result; + } + } + + + public class TShortSetting : TBaseSetting + { + public int Value; + + public TShortSetting(string Designator, string Name, int Value) + { + this.Designator = Designator; + this.Name = Name; + this.Value = Value; + } + + public override object Clone() + { + return new TShortSetting(Designator, Name, Value); + } + + public override string GetValueAsString() + { + return Value.ToString(); + } + + public override void SetValueFromString(string Text) + { + int.TryParse(Text, out this.Value); + } + } + + public class TSetting : TShortSetting + { /// /// null if range unknown /// public TRange Range; - public int Value; /// /// null if options unknown /// @@ -717,6 +1531,7 @@ public class TSetting public TSetting(string Designator, string Name, TRange Range, int Value, TOption[] Options, int Increment) + : base(Designator, Name, Value) { this.Designator = Designator; this.Name = Name; @@ -726,279 +1541,991 @@ public TSetting(string Designator, string Name, TRange Range, int Value, TOption this.Increment = Increment; } - public string[] GetOptionNames() + public override object Clone() + { + return new TSetting(Designator, Name, Range, Value, Options, Increment); + } + + public string[] GetOptionNames() + { + if (Options == null) + { + return null; + } + else + { + return RFDLib.Array.CherryPickArray(Options, (x) => x.OptionName); + } + } + + public string GetOptionNameForValue(string Value) + { + if (Options != null) + { + foreach (var O in Options) + { + if (O.Value.ToString() == Value) + { + return O.OptionName; + } + } + } + return null; + } + + /// + /// Returns whether this setting is a flag/boolean. + /// + /// + public bool GetIsFlag() + { + return (Range != null) && Range.GetOptions().Length == 2 && + Range.GetOptions()[0] == 0 && Range.GetOptions()[1] == 1; + } + + public abstract class TRange + { + public abstract int[] GetOptions(); + + /// + /// Get the list of options and include the option with the given value if it doesnt exist. + /// + /// The value to include. + /// The options. Never null. + public int[] GetOptionsIncludingValue(int Value) + { + List Result = new List(); + bool GotValue = false; + + foreach (var n in GetOptions()) + { + if (n == Value) + { + GotValue = true; + } + if (!GotValue) + { + if (n > Value) + { + Result.Add(Value); + GotValue = true; + } + } + + Result.Add(n); + } + + if (!GotValue) + { + Result.Add(Value); + } + + return Result.ToArray(); + } + } + + public class TSimpleRange : TRange + { + public readonly int Min; + public readonly int Max; + public readonly int Increment; + + public TSimpleRange(int Min, int Max, int Increment) + { + this.Min = Min; + this.Max = Max; + this.Increment = Increment; + } + + public override int[] GetOptions() + { + int[] list; + int index = 0; + bool GotEnd = false; + + int Min = this.Min; + int Max = this.Max; + + //Prevent exception for the case of the modem firmware erroneously specifiying a range in which max is less than min. + if (Max < Min) + { + Max = Min; + } + + if (Min == Max) + { + list = new int[1]; + } + else + { + list = new int[((Max - Min - 1) / Increment) + 2]; + } + + for (var a = Min; a <= Max; a += Increment) + { + if (a == Max) + { + GotEnd = true; + } + list[index++] = a; + } + + if (!GotEnd) + { + list[index++] = Max; + } + + return list; + } + } + + public class TMultiRange : TRange + { + TSimpleRange[] _SimpleRanges; + + public TMultiRange(TSimpleRange[] SimpleRanges) + { + _SimpleRanges = SimpleRanges; + } + + public override int[] GetOptions() + { + int TotalLength = 0; + int SRIndex; + + for (SRIndex = 0; SRIndex < _SimpleRanges.Length; SRIndex++) + { + TotalLength += _SimpleRanges[SRIndex].GetOptions().Length; + } + + int[] Result = new int[TotalLength]; + int ResultIndex = 0; + + for (SRIndex = 0; SRIndex < _SimpleRanges.Length; SRIndex++) + { + int[] SubResult = _SimpleRanges[SRIndex].GetOptions(); + Array.Copy(SubResult, 0, Result, ResultIndex, SubResult.Length); + ResultIndex += SubResult.Length; + } + + return Result; + } + } + + public class TOption + { + public readonly int Value; + public readonly string OptionName; + + public TOption(int Value, string OptionName) + { + this.Value = Value; + this.OptionName = OptionName; + } + } + } + + public abstract class RFD900 + { + protected TSession _Session; + bool _IsDINIO = false; + + public RFD900(TSession Session) + { + _Session = Session; + } + + /// + /// Returns whether the firmware model is allowed to be different to the connected modem model. + /// + /// + bool FirmwareDiffExempt() + { + return false; + } + + bool CheckFirmwareIncludingFileNameOK(string FilePath) + { + try + { + return (!IsDINIO || System.IO.Path.GetFileName(FilePath).Contains("DINIO")) && CheckFirmwareOK(FilePath); + } + catch + { + return CheckFirmwareOK(FilePath); + } + } + + public Uploader.Frequency GetBand() + { + try + { + var FreqString = _Session.ATCClient.DoQuery("ATI3", true); + if (FreqString.Contains("]")) + { + FreqString = FreqString.Split(']')[1]; + } + + System.Globalization.NumberStyles style = System.Globalization.NumberStyles.Any; + + if (FreqString.ToLower().Contains("x")) + { + style = System.Globalization.NumberStyles.AllowHexSpecifier; + } + + var freq = + (Uploader.Frequency) + Enum.Parse(typeof(Uploader.Frequency), + int.Parse(FreqString.ToLower().Replace("x", ""), style).ToString()); + + return freq; + } + catch + { + return Uploader.Frequency.FREQ_NONE; + } + } + + /// + /// Program firmware into modem, firstly doing necessary checks. + /// + /// The path of the firmware. Must not be null. + /// A callback to report progress. + /// true if succeeded, false if failed. + public virtual bool ProgramFirmware(string FilePath, Action Progress) + { + if (CheckFirmwareIncludingFileNameOK(FilePath) || FirmwareDiffExempt()) + { + Progress("Putting into bootloader mode.", double.NaN); + var Mode = _Session.PutIntoBootloaderMode(); + switch (Mode) + { + case TSession.TMode.BOOTLOADER: + case TSession.TMode.BOOTLOADER_X: + Progress("Programming selected firmware into modem.", double.NaN); + return DoFirmwareProgramming(FilePath, Progress); + default: + Progress("Failed to put into bootloader mode.", double.NaN); + return false; + } + } + else + { + Progress("Incorrect firmware selected.", double.NaN); + System.Threading.Thread.Sleep(2000); + return false; + } + } + + /// + /// The function which actually programs the firmware, doesn't check the firmware first, etc. + /// + /// The firmware file path. Must not be null. + /// The programming progress callback. Must not be null. + /// true if successful, false if failed. + protected abstract bool DoFirmwareProgramming(string FilePath, Action Progress); + protected abstract bool CheckFirmwareOK(string FilePath); + + static bool SearchTokenUpdate(byte[] Token, ref int TokenIndex, byte NextByte) + { + if (NextByte == Token[TokenIndex]) + { + if (++TokenIndex >= Token.Length) + { + //Found it. + return true; + } + } + else + { + TokenIndex = 0; + } + return false; + } + + /// + /// Search the given binary file for the given tokens + /// + /// The binary file path. Must not be null. + /// The tokens. Must not be null. + /// true if at least one token found, false if not. + protected static bool SearchBinary(string BinFilePath, string[] Tokens) + { + foreach (var Token in Tokens) + { + byte[] BinToken = System.Text.ASCIIEncoding.ASCII.GetBytes(Token); + + using (System.IO.FileStream FS = new System.IO.FileStream(BinFilePath, System.IO.FileMode.Open)) + { + int Byte; + int TokenIndex = 0; + while ((Byte = FS.ReadByte()) != -1) + { + if (SearchTokenUpdate(BinToken, ref TokenIndex, (byte)Byte)) + { + return true; + } + } + } + } + + return false; + } + + /// + /// Search the given hex file for the given tokens. + /// + /// The file to search. Must not be null. + /// The tokens to search for. Must not be null. + /// true if at least one found, false if not found. + protected static bool SearchHex(IHex File, string[] Tokens) + { + foreach (var Token in Tokens) + { + int TokenIndex = 0; + byte[] BinToken = System.Text.ASCIIEncoding.ASCII.GetBytes(Token); + + foreach (var Part in File.Values) + { + foreach (byte b in Part) + { + if (SearchTokenUpdate(BinToken, ref TokenIndex, b)) + { + return true; + } + } + } + } + return false; + } + + protected abstract string[] GetFirmwareSearchTokens(); + + protected void ShowWrongFirmwareMessageBox() + { + { + string S = "File doesn't appear to be valid for this radio. Could not find "; + var Tokens = GetFirmwareSearchTokens(); + for (int n = 0; n < Tokens.Length; n++) + { + if (n != 0) + { + if (n >= (Tokens.Length - 1)) + { + S += " or "; + } + else + { + S += ", "; + } + } + S += "\"" + Tokens[n] + "\""; + } + + S += " in File."; + + System.Windows.Forms.MessageBox.Show(S); + } + } + + public abstract Uploader.Board Board { get; } + + public bool IsDINIO + { + get + { + return _IsDINIO; + } + } + + public abstract string[] FirmwareFileNameExtensions { get; } + + public void SetDINIO() + { + _IsDINIO = true; + } + + /// + /// Tries to extract the firmware version string from the ATI command response. + /// + /// ATI(/RTI) command response. Must not be null. + /// The version string, if found, otherwise null. + public static string ATIResponseToFWVersion(string ATIResponse) + { + if (ATIResponse.Contains("RFD") && ATIResponse.Contains("on")) + { + int RFD = ATIResponse.IndexOf("RFD"); + int on = ATIResponse.IndexOf("on"); + + if (on > RFD) + { + return ATIResponse.Substring(RFD + 3, on - RFD - 3).Trim(); + } + } + + return null; + } + } + + public abstract class RFD900APU : RFD900 + { + public RFD900APU(TSession Session) + : base(Session) + { + + } + + /// + /// Check whether the firmware in the given path is suitable for this modem. + /// + /// The path to the firmware. Must not be null. + /// true if OK, false if not. + protected override bool CheckFirmwareOK(string FilePath) + { + try + { + IHex Hex = new IHex(); + Hex.load(FilePath); + + if (SearchHex(Hex, GetFirmwareSearchTokens())) + { + return true; + } + else + { + ShowWrongFirmwareMessageBox(); + return false; + } + } + catch + { + return false; + } + } + + /// + /// Do the firmware programming without doing any checks. + /// + /// The path of the firmware file to program. Must not be null. + /// A callback for progress. + /// true if succeeded, false if failed. + protected override bool DoFirmwareProgramming(string FilePath, Action Progress) { - if (Options == null) + try { - return null; + IHex Hex = new IHex(); + Hex.load(FilePath); + + Uploader UL = new Uploader(); + UL.ProgressEvent += (d) => Progress(null, d); + UL.upload(_Session.Port, Hex); + _Session.AssumeMode(TSession.TMode.TRANSPARENT); + return true; } - else + catch { - return RFDLib.Array.CherryPickArray(Options, (x) => x.OptionName); + return false; } } - public string GetOptionNameForValue(string Value) + public override string[] FirmwareFileNameExtensions + { + get + { + return new string[] { "hex", "ihx" }; + } + } + + /// + /// Get the correct object for the modem which is in bootloader mode. + /// + /// The session. Must not be null. + /// null if could not generate modem object + public static RFD900APU GetObjectForModem(TSession Session) { - if (Options != null) + Uploader U = new Uploader(); + U.port = Session.Port; + Uploader.Board Board = Uploader.Board.FAILED; + Uploader.Frequency Freq = Uploader.Frequency.FAILED; + + U.getDevice(ref Board, ref Freq); + switch (Board) { - foreach (var O in Options) - { - if (O.Value.ToString() == Value) - { - return O.OptionName; - } - } + case Uploader.Board.DEVICE_ID_RFD900A: + return new RFD900a(Session); + case Uploader.Board.DEVICE_ID_RFD900P: + return new RFD900p(Session); + case Uploader.Board.DEVICE_ID_RFD900U: + return new RFD900u(Session); + default: + return null; } - return null; } + } - public class TRange + public class RFD900a : RFD900APU + { + public RFD900a(TSession Session) + : base(Session) { - public readonly int Min; - public readonly int Max; - public TRange(int Min, int Max) - { - this.Min = Min; - this.Max = Max; - } } - public class TOption + protected override string[] GetFirmwareSearchTokens() { - public readonly int Value; - public readonly string OptionName; + return new string[] { "RFD900A" }; + } - public TOption(int Value, string OptionName) + public override Uploader.Board Board + { + get { - this.Value = Value; - this.OptionName = OptionName; + return Uploader.Board.DEVICE_ID_RFD900A; } } } - public abstract class RFD900 + public class RFD900p : RFD900APU { - protected TSession _Session; - - public RFD900(TSession Session) + public RFD900p(TSession Session) + : base(Session) { - _Session = Session; + } - public abstract bool ProgramFirmware(string FilePath, Action Progress); + protected override string[] GetFirmwareSearchTokens() + { + return new string[] { "RFD900P" }; + } - static bool SearchTokenUpdate(byte[] Token, ref int TokenIndex, byte NextByte) + public override Uploader.Board Board { - if (NextByte == Token[TokenIndex]) + get { - if (++TokenIndex >= Token.Length) - { - //Found it. - return true; - } + return Uploader.Board.DEVICE_ID_RFD900P; } - else + } + } + + public class RFD900u : RFD900APU + { + public RFD900u(TSession Session) + : base(Session) + { + + } + + protected override string[] GetFirmwareSearchTokens() + { + return new string[] { "RFD900U" }; + } + + public override Uploader.Board Board + { + get { - TokenIndex = 0; + return Uploader.Board.DEVICE_ID_RFD900U; } - return false; + } + } + + public abstract class RFD900xuxRevN : RFD900 + { + public RFD900xuxRevN(TSession Session) + : base(Session) + { } /// - /// Search the given binary file for the given tokens + /// Get the correct object for the modem which is in bootloader mode. /// - /// The binary file path. Must not be null. - /// The tokens. Must not be null. - /// true if at least one token found, false if not. - protected static bool SearchBinary(string BinFilePath, string[] Tokens) - { - foreach (var Token in Tokens) + /// The session. Must not be null. + /// null if could not generate modem object + public static RFD900xuxRevN GetObjectForModem(TSession Session) + { + Session.Port.Write("\r\n"); + Thread.Sleep(200); + Session.Port.DiscardInBuffer(); + Session.Port.Write("CHIPID\r\n"); + int TokenIndex = Session.WaitForAnyOfTheseTokens(new string[] { "RFD900xSub:1", "RFD900uxSub:1", "RFD900xSub:2", "RFD900uxSub:2"}, 200); + switch (TokenIndex) + { + case 0: + return new RFD900x(Session); + case 1: + return new RFD900ux(Session); + case 2: + return new RFD900X2(Session); + case 3: + return new RFD900UX2(Session); + default: + return null; + } + } + + static bool GetIsLetter(char x) + { + return (((x >= 'a') && (x <= 'z')) || ((x >= 'A') && (x <= 'Z'))); + } + + /// + /// Gets remote country code. + /// + /// + public TCountry GetRemoteCountryCode() + { + return GetCountryCode("R"); + } + + protected abstract int GetCountryCodeRegPosition(); + + TCountry GetCountryCode(string AorR) + { + string Reply = _Session.ATCClient.DoQuery(AorR + "T+C" + GetCountryCodeRegPosition().ToString() + "?", true); + + int CCInt; + + if (int.TryParse(Reply, out CCInt)) { - byte[] BinToken = System.Text.ASCIIEncoding.ASCII.GetBytes(Token); - - using (System.IO.FileStream FS = new System.IO.FileStream(BinFilePath, System.IO.FileMode.Open)) - { - int Byte; - int TokenIndex = 0; - while ((Byte = FS.ReadByte()) != -1) - { - if (SearchTokenUpdate(BinToken, ref TokenIndex, (byte)Byte)) - { - return true; - } - } - } + return (TCountry)CCInt; + } + else + { + return TCountry.NONE; } + } - return false; + public static TCountry[] GetValidCountryLockOptionsForBand(Uploader.Frequency Band) + { + switch (Band) + { + case Uploader.Frequency.FREQ_868: + return new TCountry[] { TCountry.EU, TCountry.India }; + case Uploader.Frequency.FREQ_915: + return new TCountry[] { TCountry.AU, TCountry.US, TCountry.NZ }; + default: + return new TCountry[0]; + } } - /// - /// Search the given hex file for the given tokens. - /// - /// The file to search. Must not be null. - /// The tokens to search for. Must not be null. - /// true if at least one found, false if not found. - protected static bool SearchHex(IHex File, string[] Tokens) + public TCountry[] GetValidCountryLockOptions() + { + return GetValidCountryLockOptionsForBand(GetBand()); + } + + public static bool GetCanBeLockedToCountry(Uploader.Frequency Band, TCountry C) + { + return (new List(GetValidCountryLockOptionsForBand(Band))).Contains(C); + } + + /// + /// Gets local country code. Assumes in AT command mode already. Returns null if no country code detected. + /// + /// + public TCountry GetCountryCode() + { + return GetCountryCode("A"); + } + + public virtual bool WriteCounty(TCountry C) + { + return _Session.ATCClient.DoCommand("AT+C" + GetCountryCodeRegPosition().ToString() + "=" + ((int)C).ToString()); + } + + + /// + protected bool GetIsThisLockedToCountry() { - foreach (var Token in Tokens) + return GetIsCountryLocked(GetCountryCode()); + } + + public static bool GetIsCountryLocked(TCountry C) + { + switch (C) { - int TokenIndex = 0; - byte[] BinToken = System.Text.ASCIIEncoding.ASCII.GetBytes(Token); + case TCountry.NONE: + case TCountry.Undefined: + return false; + default: + return true; + } + } + protected virtual bool SetHigherBaud() + { + return false; + } - foreach (var Part in File.Values) - { - foreach (byte b in Part) - { - if (SearchTokenUpdate(BinToken, ref TokenIndex, b)) - { - return true; - } - } + protected virtual void ReturnToNormalBaud() + { + + } + + bool DoProgramming(string FilePath, Action Progress) + { + _Session.Port.Write("\r"); + Thread.Sleep(100); + _Session.Port.DiscardInBuffer(); + _Session.Port.Write("UPLOAD\r"); + if (_Session.WaitForToken("Ready\r\n", 5000) && _Session.WaitForToken("C", 5000)) + { + try + { + MissionPlanner.Radio.XModem.ProgressEvent += (d) => Progress(null, d); + bool Result = MissionPlanner.Radio.XModem.Upload(FilePath, _Session.Port); + _Session.AssumeMode(TSession.TMode.INIT); + return Result; + } + catch + { + return false; } } - return false; + else + { + return false; + } } - protected abstract string[] GetFirmwareSearchTokens(); + bool TryFirmwareProgrammingOnce(string FilePath, Action Progress) + { + bool Result; + bool HigherBaud; + + HigherBaud = SetHigherBaud(); + + Result = DoProgramming(FilePath, Progress); + + if (HigherBaud) + { + ReturnToNormalBaud(); + } + + return Result; + } - protected void ShowWrongFirmwareMessageBox() + /// + /// Program the given firmware without doing any checks. + /// + /// The path of the firmware file. Must not be null. + /// A function to use to provide real-time feedback about the + /// programming progress. + /// true if successful, false if failed. + protected override bool DoFirmwareProgramming(string FilePath, Action Progress) { - string S = "File doesn't appear to be valid for this radio. Could not find "; - var Tokens = GetFirmwareSearchTokens(); - for (int n = 0; n < Tokens.Length; n++) + while (!TryFirmwareProgrammingOnce(FilePath, Progress)) { - if (n != 0) + switch (System.Windows.Forms.MessageBox.Show("Programming firmware failed. Try again?", "Programming firmware failed. Try again?", System.Windows.Forms.MessageBoxButtons.YesNoCancel)) { - if (n >= (Tokens.Length - 1)) - { - S += " or "; - } - else - { - S += ", "; - } + case System.Windows.Forms.DialogResult.Yes: + break; + case System.Windows.Forms.DialogResult.No: + case System.Windows.Forms.DialogResult.Cancel: + return false; } - S += "\"" + Tokens[n] + "\""; } - - S += " in File."; - - System.Windows.Forms.MessageBox.Show(S); - } - - public abstract Uploader.Board Board { get; } - } - - public abstract class RFD900APU : RFD900 - { - public RFD900APU(TSession Session) - : base(Session) - { - + return true; } - public override bool ProgramFirmware(string FilePath, Action Progress) + /// + /// Check that the firmware is the right type for this modem. + /// + /// The path to the firmware. Must not be null. + /// true if correct, false if not. + protected override bool CheckFirmwareOK(string FilePath) { - try + if (SearchBinary(FilePath, GetFirmwareSearchTokens())) { - IHex Hex = new IHex(); - Hex.load(FilePath); - - if (SearchHex(Hex, GetFirmwareSearchTokens())) - { - Uploader UL = new Uploader(); - UL.ProgressEvent += (d) => Progress(d); - UL.upload(_Session.Port, Hex); - return true; - } - else - { - ShowWrongFirmwareMessageBox(); - return false; - } + return true; } - catch + else { + ShowWrongFirmwareMessageBox(); return false; } } + public enum TCountry + { + NONE = 0, + AU = 1, + NZ = 2, + US = 3, + EU = 4, + PRC = 5, + Ins = 6, + India = 8, + Undefined = 255, + } + } + + public abstract class RFD900xux : RFD900xuxRevN + { + public RFD900xux(TSession Session) + : base(Session) + { + } + /// - /// Get the correct object for the modem which is in bootloader mode. + /// Returns whether the file of the given path is "certified". i.e. Whether it + /// will return whether it is locked to a country using the ATI command. /// - /// The session. Must not be null. - /// null if could not generate modem object - public static RFD900APU GetObjectForModem(TSession Session) + /// The file path. Must not be null. + /// true if certified, false if not. + bool IsFirmwareCertified(string FilePath) { - Uploader U = new Uploader(); - U.port = Session.Port; - Uploader.Board Board = Uploader.Board.FAILED; - Uploader.Frequency Freq = Uploader.Frequency.FAILED; + return SearchBinary(FilePath, new string[] { "HastaLaVistaBaby" }); + } - U.getDevice(ref Board, ref Freq); - switch (Board) - { - case Uploader.Board.DEVICE_ID_RFD900A: - return new RFD900a(Session); - case Uploader.Board.DEVICE_ID_RFD900P: - return new RFD900p(Session); - case Uploader.Board.DEVICE_ID_RFD900U: - return new RFD900u(Session); - case Uploader.Board.DEVICE_ID_RFD900: - return new RFD900old(Session); - case Uploader.Board.DEVICE_ID_HM_TRP: - return new HM_TRP(Session); - case Uploader.Board.DEVICE_ID_HB1060: - return new HB1060(Session); - default: - return null; - } + protected override int GetCountryCodeRegPosition() + { + return 32; } - } - public class RFD900old : RFD900APU - { - public RFD900old(TSession Session) - : base(Session) + protected abstract string GetRefFirmwareResourcePath(); + + /// + /// Unpack the reference firmware embedded into this exe into a temp file and return + /// the path to that file. + /// + /// The path to the temp file containing the ref file. Never null. + string GetRefFirmwarePath() { + string Temp = System.IO.Path.GetTempFileName(); + var assembly = Assembly.GetExecutingAssembly(); + //var resourceName = "RFD900Tools.Properties.Resources.resources.RFDSiK_V3.00_rfd900x.bin"; + //var resourceName = "RFD900Tools.Resources.RFDSiK V3.00 rfd900x.bin"; + var resourceName = GetRefFirmwareResourcePath(); - } + var Names = assembly.GetManifestResourceNames(); + Console.WriteLine(Names.ToString()); - protected override string[] GetFirmwareSearchTokens() - { - return new string[] { "RFD900" }; - } + using (Stream stream = assembly.GetManifestResourceStream(resourceName)) + using (Stream FileStream = System.IO.File.OpenWrite(Temp)) + using (StreamReader reader = new StreamReader(stream)) + using (StreamWriter writer = new StreamWriter(FileStream)) + { + stream.Seek(0, SeekOrigin.Begin); + stream.CopyTo(FileStream); + } - public override Uploader.Board Board + return Temp; + } + + public override string[] FirmwareFileNameExtensions + { + get + { + return new string[] { "bin" }; + } + } + + public override bool WriteCounty(TCountry C) + { + return _Session.ATCClient.DoCommand("AT+C" + GetCountryCodeRegPosition().ToString() + "=" + ((int)C).ToString()); + } + + /// + /// Program the given firmware. Do necesary checks first. + /// + /// The path to the firmware file. Must not be null. + /// The function to call back to provide progress updates. + /// true if successful, false if failed. + public override bool ProgramFirmware(string FilePath, Action Progress) { - get + if (IsFirmwareCertified(FilePath)) { - return Uploader.Board.DEVICE_ID_RFD900; + return base.ProgramFirmware(FilePath, Progress); } - } - } + else + { + Progress("Trying to put into AT command mode.", double.NaN); + if (_Session.PutIntoATCommandMode() != TSession.TMode.AT_COMMAND) + { + Progress("Trying to put into bootloader mode.", double.NaN); + if (_Session.PutIntoBootloaderMode() == TSession.TMode.BOOTLOADER_X) + { + Progress("Programming temporary firmware into modem to check certified.", double.NaN); + string RefFirmwarePath = GetRefFirmwarePath(); + try + { + if (!DoFirmwareProgramming(RefFirmwarePath, Progress)) + { + return false; + } + } + finally + { + try + { + System.IO.File.Delete(RefFirmwarePath); + } + catch + { + + } + } + } + else + { + return false; + } + } - public class RFD900a : RFD900APU + //Can it go into AT command mode? + Progress("Trying to put into AT command mode.", double.NaN); + if (_Session.PutIntoATCommandMode() == TSession.TMode.AT_COMMAND) + { + Progress("Checking if modem certified.", double.NaN); + if (!GetIsThisLockedToCountry()) + { + //They can program whatever they want into it. + return base.ProgramFirmware(FilePath, Progress); + } + else + { + //They can only program certified firmware into it. + if (IsFirmwareCertified(FilePath)) + { + return base.ProgramFirmware(FilePath, Progress); + } + else + { + System.Windows.Forms.MessageBox.Show("The selected firmware is not certified to run on this modem. Aborting."); + return false; + } + } + } + else + { + return false; + } + } + } + } + + public class RFD900x : RFD900xux { - public RFD900a(TSession Session) + public RFD900x(TSession Session) : base(Session) { } + protected override string GetRefFirmwareResourcePath() + { + return "RFD900Tools.Resources.RFDSiK V3.01 rfd900x.bin"; + } + protected override string[] GetFirmwareSearchTokens() { - return new string[] { "RFD900A" }; + return new string[] { "RFD900x", "RFD900X" }; } public override Uploader.Board Board { get { - return Uploader.Board.DEVICE_ID_RFD900A; + return Uploader.Board.DEVICE_ID_RFD900X; } } } @@ -1044,157 +2571,205 @@ public override Uploader.Board Board } } - - public class RFD900p : RFD900APU + public class RFD900ux : RFD900xux { - public RFD900p(TSession Session) + public RFD900ux(TSession Session) : base(Session) { } - protected override string[] GetFirmwareSearchTokens() - { - return new string[] { "RFD900P" }; + protected override string GetRefFirmwareResourcePath() + { + return "RFD900Tools.Resources.RFDSiK V3.08 rfd900ux.bin"; } public override Uploader.Board Board { get { - return Uploader.Board.DEVICE_ID_RFD900P; + return Uploader.Board.DEVICE_ID_RFD900UX; } } + + protected override string[] GetFirmwareSearchTokens() + { + return new string[] { "RFD900ux", "RFD900UX" }; + } } - public class RFD900u : RFD900APU - { - public RFD900u(TSession Session) + /*public class RFD900sx : RFD900xux + { + public RFD900sx(TSession Session) : base(Session) { + } + + protected override string[] GetFirmwareSearchTokens() + { + return new string[] { "RFD900sx", "RFD900SX" }; + } + }*/ + public abstract class RFD900xuxRev2 : RFD900xuxRevN + { + public RFD900xuxRev2(TSession Session) + : base(Session) + { } - protected override string[] GetFirmwareSearchTokens() - { - return new string[] { "RFD900U" }; + protected override int GetCountryCodeRegPosition() + { + return 51; } - public override Uploader.Board Board - { - get - { - return Uploader.Board.DEVICE_ID_RFD900U; - } + protected override bool CheckFirmwareOK(string FilePath) + { + return true; + } + + protected override string[] GetFirmwareSearchTokens() + { + return new string[0]; + } + + protected override bool SetHigherBaud() + { + _Session.WriteToPort("BAUDHI\r\n"); + + bool Result = _Session.WaitForToken("OK", 500); + + if (Result) + { + _Session.Port.BaudRate = 1200000; + } + + return Result; + } + + protected override void ReturnToNormalBaud() + { + _Session.WriteToPort("BAUDLO\r\n"); + _Session.WaitForToken("OK", 500); + _Session.Port.BaudRate = 57600; + } + + public override bool WriteCounty(TCountry C) + { + return base.WriteCounty(C) && + _Session.ATCClient.DoCommand("AT+LC"); + } + + public override string[] FirmwareFileNameExtensions + { + get + { + return new string[] { "gbl" }; + } } } - public abstract class RFD900xux : RFD900 + public class RFD900X2 : RFD900xuxRev2 { - public RFD900xux(TSession Session) + public RFD900X2(TSession Session) : base(Session) { - } - /// - /// Get the correct object for the modem which is in bootloader mode. - /// - /// The session. Must not be null. - /// null if could not generate modem object - public static RFD900xux GetObjectForModem(TSession Session) - { - Session.Port.Write("\r\n"); - Thread.Sleep(200); - Session.Port.DiscardInBuffer(); - Session.Port.Write("CHIPID\r\n"); - int TokenIndex = Session.WaitForAnyOfTheseTokens(new string[] { "RFD900x", "RFD900ux" }, 200); - switch (TokenIndex) + public override Uploader.Board Board + { + get { - case 0: - return new RFD900x(Session); - case 1: - return new RFD900ux(Session); - default: - return null; - } + return Uploader.Board.DEVICE_ID_RFD900X2; + } } - - public override bool ProgramFirmware(string FilePath, Action Progress) + } + + public class RFD900UX2 : RFD900xuxRev2 + { + public RFD900UX2(TSession Session) + : base(Session) { - if (SearchBinary(FilePath, GetFirmwareSearchTokens())) - { - _Session.Port.Write("\r"); - Thread.Sleep(100); - _Session.Port.DiscardInBuffer(); - _Session.Port.Write("UPLOAD\r"); - if (_Session.WaitForToken("Ready\r\n", 5000) && _Session.WaitForToken("C", 5000)) - { - try - { - MissionPlanner.Radio.XModem.ProgressEvent += (d) => Progress(d); - MissionPlanner.Radio.XModem.Upload(FilePath, _Session.Port); - return true; - } - catch - { - return false; - } - } - else - { - return false; - } - } - else - { - ShowWrongFirmwareMessageBox(); - return false; - } + } + + public override Uploader.Board Board + { + get + { + return Uploader.Board.DEVICE_ID_RFD900UX2; + } } } - public class RFD900x : RFD900xux + /// + /// A RFDLib.IO.TSerialPort wrapper for TMissionPlannerSerialPort + /// + public class TMissionPlannerSerialPort : RFDLib.IO.TSerialPort { - public RFD900x(TSession Session) - : base(Session) + MissionPlanner.Comms.ICommsSerial _Port; + + public TMissionPlannerSerialPort(MissionPlanner.Comms.ICommsSerial Port) + { + _Port = Port; + } + + public override void Write(string Text) { + _Port.Write(Text); + } + public override void DiscardInBuffer() + { + _Port.DiscardInBuffer(); } - protected override string[] GetFirmwareSearchTokens() + public override int ReadChar() { - return new string[] { "RFD900x", "RFD900X" }; + return _Port.ReadChar(); } - public override Uploader.Board Board + public override string ReadExisting() + { + return _Port.ReadExisting(); + } + + public override string ReadLine() + { + return _Port.ReadLine(); + } + + public override int ReadTimeout { get { - return Uploader.Board.DEVICE_ID_RFD900X; + return _Port.ReadTimeout; + } + set + { + _Port.ReadTimeout = value; } } } - public class RFD900ux : RFD900xux + public class TATCClient : RFDLib.IO.ATCommand.TClient { - public RFD900ux(TSession Session) - : base(Session) - { - + public TATCClient(RFDLib.IO.TSerialPort Port) + : base(Port) + { } - public override Uploader.Board Board + public override string DoQuery(string Command, bool WaitForTerminator) { - get + string Raw = base.DoQuery(Command, WaitForTerminator); + + if (Raw.StartsWith("[") && Raw.Contains("]") && ((Raw.IndexOf(']') + 1) < Raw.Length)) { - return Uploader.Board.DEVICE_ID_RFD900UX; + return Raw.Substring(Raw.IndexOf(']') + 1); + } + else + { + return Raw; } - } - - protected override string[] GetFirmwareSearchTokens() - { - return new string[] { "RFD900ux", "RFD900UX" }; } } } \ No newline at end of file diff --git a/SikRadio/RFDLib/Array.cs b/SikRadio/RFDLib/Array.cs index 213f1163cf..7f4da5be74 100644 --- a/SikRadio/RFDLib/Array.cs +++ b/SikRadio/RFDLib/Array.cs @@ -13,6 +13,30 @@ public static T[] CherryPickArray(U[] x, Func PickFn) Result[n] = PickFn(x[n]); } return Result; + } + + /// + /// Copy the given array, removing elements which meet the given condition. + /// + /// + /// + /// + /// + public static T[] ConditionalRemove(T[] x, Func Condition) + { + T[] Temp = new T[x.Length]; + int Length = 0; + foreach (var a in x) + { + if (!Condition(a)) + { + Temp[Length++] = a; + } + } + + T[] Result = new T[Length]; + System.Array.Copy(Temp, Result, Length); + return Result; } } } diff --git a/SikRadio/RFDLib/Collections.cs b/SikRadio/RFDLib/Collections.cs new file mode 100644 index 0000000000..3db3b7b1a5 --- /dev/null +++ b/SikRadio/RFDLib/Collections.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; + +namespace RFDLib +{ + public static class Collections + { + public static Dictionary Translate(Dictionary x, Func TransLateFn) + { + Dictionary Result = new Dictionary(); + + foreach (var kvp in x) + { + Result[kvp.Key] = TransLateFn(kvp.Value); + } + + return Result; + } + + /// + /// Update the given original dictionary with new values from NewValues. + /// + /// + /// + /// + /// + public static void Update(Dictionary Original, Dictionary NewValues) + { + foreach (var kvp in NewValues) + { + Original[kvp.Key] = kvp.Value; + } + } + } + +} \ No newline at end of file diff --git a/SikRadio/RFDLib/GUI/Settings.cs b/SikRadio/RFDLib/GUI/Settings.cs new file mode 100644 index 0000000000..183c0582c7 --- /dev/null +++ b/SikRadio/RFDLib/GUI/Settings.cs @@ -0,0 +1,262 @@ +using System; +using System.Collections.Generic; +using System.Windows.Forms; + +namespace RFDLib.GUI.Settings +{ + public class TLabelEditorPair + { + public readonly Label L; + public readonly Control Editor; + + public TLabelEditorPair(Label L, Control Editor) + { + this.L = L; + this.Editor = Editor; + } + + public virtual string[] SettingNames + { + get + { + return new string[] { Editor.Name }; + } + } + } + + /// + /// A TLabelEditorPair which remembers the original name and label. + /// + public class TOrigLabelEditorPair : TLabelEditorPair + { + public readonly string OriginalLabel; + public readonly string OriginalName; + public readonly bool OrigVisible; + public readonly ToolTip OrigToolTip; + public readonly string OrigToolTipText; + + public TOrigLabelEditorPair(Label L, Control Editor, ToolTip TT) + : base(L, Editor) + { + this.OriginalLabel = L.Text; + this.OriginalName = Editor.Name; + this.OrigVisible = Editor.Visible; + this.OrigToolTip = TT; + this.OrigToolTipText = TT.GetToolTip(Editor); + } + + /// + /// Reset to original values. + /// + public void Reset() + { + L.Text = OriginalLabel; + Editor.Name = OriginalName; + L.Visible = OrigVisible; + Editor.Visible = OrigVisible; + OrigToolTip.SetToolTip(Editor, OrigToolTipText); + } + } + + /// + /// This is for dynamically re-using spare label-editor pairs for which there is no setting in the modem. + /// + public class TLabelEditorPairRegister + { + Dictionary _Pairs = new Dictionary(); + Dictionary _Spare = new Dictionary(); + + /// + /// Add a label-editor pair. + /// + /// + public void Add(TOrigLabelEditorPair Pair) + { + _Pairs[Pair.OriginalName] = Pair; + } + + /// + /// Add a label-editor pair. + /// + /// + /// + public void Add(Label L, Control Editor, ToolTip TT) + { + Add(new TOrigLabelEditorPair(L, Editor, TT)); + } + + /// + /// Reset all label-editor pairs to their original labels and names. + /// + public void Reset() + { + foreach (var P in _Pairs.Values) + { + P.Reset(); + } + + _Spare.Clear(); + } + + /// + /// Call this when the list of setting names has been read from the modem, so the spare + /// label-editor pairs can be put into a separate list. + /// + /// The list of setting names read from the modem. Must not be null. + public void SetUp(List SettingNamesSet) + { + foreach (var kvp in _Pairs) + { + if (!SettingNamesSet.Contains(kvp.Value.Editor.Name)) + { + _Spare[kvp.Key] = kvp.Value; + } + } + } + + /// + /// Get a spare editor control for an unexpected setting read from the modem. Set the editor's label + /// to the setting name, if a spare editor was found. + /// + /// The setting name. Must not be null. + /// The condition the editor control must meet. Must not be null. + /// + Control GetSpare(string SettingName, Func Condition, string Description) + { + TOrigLabelEditorPair Pair = null; + + foreach (var kvp in _Spare) + { + if (Condition(kvp.Value.Editor)) + { + //Assign to a variable and break, instead of processing here, because + //part of the processing is removing it from _Spare, but _Spare is being iterated here. + Pair = kvp.Value; + break; + } + } + + if (Pair == null) + { + return null; + } + else + { + Pair.Editor.Name = SettingName; + Pair.L.Text = Description; + if (Pair.L.Bottom > Pair.Editor.Top) + { + while (Pair.L.Width > (Pair.Editor.Left - Pair.L.Left) && Pair.L.Font.Size > 1) + { + Pair.L.Font = new System.Drawing.Font(Pair.L.Font.FontFamily, Pair.L.Font.Size - 1); + } + } + Pair.L.Visible = true; + Pair.Editor.Visible = true; + Pair.OrigToolTip.SetToolTip(Pair.Editor, Description); + _Spare.Remove(Pair.OriginalName); + return Pair.Editor; + } + } + + /// + /// Return a spare combobox if there is one, otherwise return null. + /// + /// + /// + public ComboBox GetSpareComboBox(string SettingName, string Description) + { + return (ComboBox)GetSpare(SettingName, (C) => C is ComboBox, Description); + } + + /// + /// Return a spare checkbox if there is one, otherwise return null. + /// + /// + /// + public CheckBox GetSpareCheckbox(string SettingName, string Description) + { + return (CheckBox)GetSpare(SettingName, (C) => C is CheckBox, Description); + } + } + + public class TDynamicLabelEditorPair : TLabelEditorPair + { + public readonly TSettingNameLabelTextPair[] AlternateNames; + + public TDynamicLabelEditorPair(Label L, Control Editor, TSettingNameLabelTextPair[] AlternateNames) + : base(L, Editor) + { + this.AlternateNames = AlternateNames; + } + + public override string[] SettingNames + { + get + { + string[] Result = new string[AlternateNames.Length]; + + for (int n = 0; n < AlternateNames.Length; n++) + { + Result[n] = AlternateNames[n].SettingName; + } + + return Result; + } + } + + public bool SetUpForSettingName(string SettingName) + { + foreach (var SNLTP in AlternateNames) + { + if (SNLTP.SettingName == SettingName) + { + L.Text = SNLTP.LabelText; + return true; + } + } + + return false; + } + + public class TSettingNameLabelTextPair + { + public readonly string SettingName; + public readonly string LabelText; + + public TSettingNameLabelTextPair(string SettingName, string LabelText) + { + this.SettingName = SettingName; + this.LabelText = LabelText; + } + } + } + + public class TDynamicLabelEditorPairRegister + { + public readonly TDynamicLabelEditorPair[] Pairs; + + public TDynamicLabelEditorPairRegister(TDynamicLabelEditorPair[] Pairs) + { + this.Pairs = Pairs; + } + + /// + /// Try to find an editor control for the setting of the given name, and if found, set up its label. + /// Returns the editor control if successful, otherwise returns null. + /// + /// The setting name. Must not be null. + /// Returns the editor control if successful, otherwise returns null. + public Control FindAndSetUpEditorWithSettingName(string SettingName) + { + foreach (var Pair in Pairs) + { + if (Pair.SetUpForSettingName(SettingName)) + { + return Pair.Editor; + } + } + return null; + } + } +} \ No newline at end of file diff --git a/SikRadio/RFDLib/IO/ATCommand.cs b/SikRadio/RFDLib/IO/ATCommand.cs new file mode 100644 index 0000000000..5aa01b105d --- /dev/null +++ b/SikRadio/RFDLib/IO/ATCommand.cs @@ -0,0 +1,209 @@ +using System; + +namespace RFDLib.IO.ATCommand +{ + /// + /// An AT-command interface client. + /// + public class TClient + { + public IO.TSerialPort _Port; + /// + /// The line terminator. null if nothing. + /// + public string Terminator = "\r\n"; + /// + /// true if the server echoes commands, and the echoes need to be ignored. + /// + public bool Echoes = true; + /// + /// The maximum time to wait for a response. + /// + public int Timeout = 2000; + + /// + /// Create a new TClient + /// + /// The serial port to use. Must not be null. + public TClient(IO.TSerialPort Port) + { + _Port = Port; + } + + /// + /// Returns the terminator to use. + /// + /// The terminator to use, never null. + string GetTerminator() + { + if (Terminator == null) + { + return ""; + } + else + { + return Terminator; + } + } + + /// + /// Get next char from serial port. Wait up to the read timeout. + /// + /// the char read + /// true if got char before timeout, false if not. + bool GetNextChar(out char x) + { + try + { + x = (char)_Port.ReadChar(); + return true; + } + catch + { + x = '\0'; + return false; + } + } + + /// + /// Eliminate the command echo. + /// + /// The complete command including terminator. Must not be null. + /// true if got echo OK, false if not. + bool EliminateEcho(string CompleteCommand) + { + if (Echoes) + { + _Port.ReadTimeout = Timeout; + int n = 0; + char Temp; + while ((n < CompleteCommand.Length) && GetNextChar(out Temp)) + { + if (CompleteCommand[n] == Temp) + { + n++; + } + else + { + return false; + } + } + } + return true; + } + + /// + /// Do a command and wait for a reply. Returns true if "OK" + /// + /// + /// + public bool DoCommand(string Command) + { + string Result = DoQuery(Command, true); + return Result.Contains("OK"); + } + + /// + /// Returns a received line from the interface. + /// + /// + public string GetLine() + { + return _Port.ReadLine(); + } + + /// + /// Do the given command. If wait for terminator, returns the result minus + /// the terminator. + /// + /// The command. Must not be null. + /// true to wait for terminator. + /// The reply, except for the echo. + public virtual string DoQuery(string Command, bool WaitForTerminator) + { + string CompleteCommand = Command + GetTerminator(); + _Port.DiscardInBuffer(); + _Port.Write(CompleteCommand); + if (EliminateEcho(CompleteCommand)) + { + string Result; + if (WaitForTerminator) + { + if (IO.TSerialPort.WaitForToken(_Port, Terminator, Timeout, out Result)) + { + //Get the terminator out of it. + return Result.Split(new string[] { Terminator }, StringSplitOptions.None)[0]; + } + else + { + return Result; + } + } + else + { + IO.TSerialPort.WaitForToken(_Port, null, Timeout, out Result); + return Result; + } + } + else + { + return ""; + } + } + + public string DoQueryWithMultiLineResponse(string Command) + { + string CompleteCommand = Command + GetTerminator(); + _Port.DiscardInBuffer(); + _Port.Write(CompleteCommand); + + if (EliminateEcho(CompleteCommand)) + { + string Result = ""; + string Temp; + int Timeout = this.Timeout; + + while (IO.TSerialPort.WaitForToken(_Port, Terminator, Timeout, out Temp)) + { + Result += Temp; + Timeout = 500; + } + + return Result; + } + else + { + return ""; + } + } + } + + public class TServer + { + TSerialPort _Port; + public event Action GotCommand; + + public TServer(TSerialPort Port) + { + _Port = Port; + } + + public abstract class TCommand + { + } + + public class TQuery : TCommand + { + public char FirstCharacter; + public string Parameter; + } + + public class TSetValue : TCommand + { + public char FirstCharacter; + public string Parameter; + public string Value; + } + + } +} \ No newline at end of file diff --git a/SikRadio/RFDLib/IO/SerialPort/SerialPort.cs b/SikRadio/RFDLib/IO/SerialPort/SerialPort.cs new file mode 100644 index 0000000000..a2ea7f4c55 --- /dev/null +++ b/SikRadio/RFDLib/IO/SerialPort/SerialPort.cs @@ -0,0 +1,137 @@ +using System; + +namespace RFDLib.IO +{ + public abstract class TSerialPort + { + public abstract void Write(string Text); + public abstract string ReadExisting(); + public abstract int ReadTimeout { get; set; } + public abstract int ReadChar(); + public abstract string ReadLine(); + public abstract void DiscardInBuffer(); + + /// + /// Query a serial port. + /// + /// The port. Must not be null. + /// The query to send, without the terminator. Must not be null. + /// The terminator. Can be null if none. + /// The maximum period to wait in milliseconds. + /// The data returned. Never null. + public static string Query(TSerialPort Port, + string Query, string Terminator, int MaxWait) + { + Port.DiscardInBuffer(); + Query = Query + (Terminator == null ? "" : Terminator); + Port.Write(Query); + string Reply; + WaitForToken(Port, Terminator, MaxWait, out Reply); + return Reply; + } + + /// + /// Wait for the given token or timeout on the given serial port. + /// + /// The port. Must not be null. + /// The token to wait for. Can be null if not waiting for a token. + /// The maximum amount of time to wait for a response, in milliseconds. + /// true if token was received, false if not. + public static bool WaitForToken(TSerialPort Port, + string Token, int MaxWait) + { + string Temp; + return WaitForToken(Port, Token, MaxWait, out Temp); + } + + /// + /// Wait for the given token or timeout on the given serial port + /// + /// The port. Must not be null. + /// The token to wait for. Can be null if not waiting for a token. + /// The maximum amount of time to wait for a response, in milliseconds. + /// The data returned. Never null. + /// true if token was received, false if not. + public static bool WaitForToken(TSerialPort Port, + string Token, int MaxWait, out string Result) + { + System.Diagnostics.Stopwatch SW = new System.Diagnostics.Stopwatch(); + string Temp = ""; + int Timeout = Port.ReadTimeout; + Port.ReadTimeout = MaxWait; + SW.Start(); + + while (SW.ElapsedMilliseconds < MaxWait) + { + //System.Diagnostics.Stopwatch StWa = new System.Diagnostics.Stopwatch(); + //SW.Start(); + string x = Port.ReadExisting(); + //if (StWa.ElapsedMilliseconds > 10) + //{ + // System.Diagnostics.Debug.WriteLine("Read existing slow\n"); + //} + Temp += x; + + if ((Token != null) && Temp.Contains(Token)) + { + Port.ReadTimeout = Timeout; + Result = Temp; + return true; + } + + System.Threading.Thread.Sleep(50); + } + Port.ReadTimeout = Timeout; + Result = Temp; + return false; + } + } + + public class TSystemIOPortsSerialPort : TSerialPort + { + System.IO.Ports.SerialPort _Port; + + public TSystemIOPortsSerialPort(System.IO.Ports.SerialPort Port) + { + _Port = Port; + } + + public override void DiscardInBuffer() + { + _Port.DiscardInBuffer(); + } + + public override int ReadChar() + { + return _Port.ReadChar(); + } + + public override void Write(string Text) + { + _Port.Write(Text); + } + + public override string ReadLine() + { + return _Port.ReadLine(); + } + + public override string ReadExisting() + { + return _Port.ReadExisting(); + } + + public override int ReadTimeout + { + get + { + return _Port.ReadTimeout; + } + set + { + _Port.ReadTimeout = value; + } + } + } + +} \ No newline at end of file diff --git a/SikRadio/RFDLib/RFDLib.cs b/SikRadio/RFDLib/RFDLib.cs new file mode 100644 index 0000000000..565554647e --- /dev/null +++ b/SikRadio/RFDLib/RFDLib.cs @@ -0,0 +1,49 @@ +using System; + + +namespace RFDLib +{ + + public static class Utils + { + + public static bool Retry(Func ToTry, Func PassedCondition, int QTYTries) + { + int TryCount; + + for (TryCount = 0; TryCount < QTYTries; TryCount++) + { + if (PassedCondition(ToTry())) + { + return true; + } + } + + return false; + } + + public static bool Retry(Func PassedCondition, int QTYTries) + { + return Retry(() => (int)1, (x) => PassedCondition(), QTYTries); + } + + public static System.Collections.Generic.KeyValuePair[] EnumToStrings(Type EnumType) + { + var Values = Enum.GetValues(EnumType); + System.Collections.Generic.KeyValuePair[] Result = + new System.Collections.Generic.KeyValuePair[Values.Length]; + + for (int n = 0; n < Values.Length; n++) + { + Result[n] = new System.Collections.Generic.KeyValuePair( + Enum.GetName(EnumType, Values.GetValue(n)), (int)Values.GetValue(n)); + + } + + return Result; + } + + } + + +} \ No newline at end of file diff --git a/SikRadio/RFDLib/Text.cs b/SikRadio/RFDLib/Text.cs index 57fcfb8e72..9cba4abd0d 100644 --- a/SikRadio/RFDLib/Text.cs +++ b/SikRadio/RFDLib/Text.cs @@ -3,7 +3,18 @@ namespace RFDLib { public static class Text - { + { + /// + /// Case-insensitive contains. + /// + /// + /// + /// + public static bool Contains(string Haystack, string Needle) + { + return Haystack.IndexOf(Needle, StringComparison.OrdinalIgnoreCase) >= 0; + } + /// /// Returns whether the given character is an upper or lower case letter of the alphabet. /// @@ -23,5 +34,10 @@ public static bool CheckIsNumeral(char c) { return (c >= '0' && c <= '9'); } + + public static bool CheckIsHexNumeral(char c) + { + return CheckIsNumeral(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); + } } } \ No newline at end of file diff --git a/SikRadio/Resources/RFDSiK V3.00 rfd900x.bin b/SikRadio/Resources/RFDSiK V3.00 rfd900x.bin new file mode 100644 index 0000000000000000000000000000000000000000..c360ac31c31062fa9cd46a17d532c29616d9b449 GIT binary patch literal 110288 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≥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

=Sa8HI%z!>3N4}j1Cd#>zX@mmTL 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%0AOS#EyD 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 + { + Session = new RFD.RFD900.TSession(SikRadio.Config.comPort, MainV2.comPort.BaseStream.BaudRate); + return Session.PutIntoATCommandMode() == RFD.RFD900.TSession.TMode.AT_COMMAND; + } + , 3)) { - inter.doCommand(Config.comPort, "AT&T=RSSI"); + if (RFDLib.Utils.Retry(() => Session.ATCClient.DoQuery("AT&T=RSSI", true).Contains("RSSI"), 3)) + { + Session.AssumeMode(RFD.RFD900.TSession.TMode.TRANSPARENT); - _Session.AssumeMode(RFD.RFD900.TSession.TMode.TRANSPARENT); + tickStart = Environment.TickCount; - tickStart = Environment.TickCount; + timer1.Start(); - timer1.Start(); + _Session = Session; + } + else + { + var ATIReply = Session.ATCClient.DoQuery("ATI", true); + if (RFDLib.Text.Contains(ATIReply, "async")) + { + MissionPlanner.MsgBox.CustomMessageBox.Show("Firmware doesn't support RSSI reporting"); + } + else + { + MissionPlanner.MsgBox.CustomMessageBox.Show("Failed to enter RSSI reporting mode."); + } + } + } + else + { + MissionPlanner.MsgBox.CustomMessageBox.Show("Failed to put modem into AT command mode."); } } } @@ -70,62 +94,10 @@ public void Disconnect() _Session.Dispose(); _Session = null; - - BUT_disconnect.Enabled = false; - BUT_connect.Enabled = true; } } } - private void BUT_connect_Click(object sender, EventArgs e) - { - CustomMessageBox.Show("Ensure you disconnect properly, to leave the radio in a good state"); - - try - { - MainV2.comPort.BaseStream.Open(); - - inter.doConnect(MainV2.comPort.BaseStream); - - inter.doCommand(MainV2.comPort.BaseStream, "AT&T=RSSI"); - - inter.doCommand(MainV2.comPort.BaseStream, "ATO"); - - tickStart = Environment.TickCount; - - timer1.Start(); - - BUT_disconnect.Enabled = true; - BUT_connect.Enabled = false; - } - catch - { - CustomMessageBox.Show("Bad Port Setting"); - } - } - - private void BUT_disconnect_Click(object sender, EventArgs e) - { - try - { - timer1.Stop(); - - inter.doConnect(MainV2.comPort.BaseStream); - - inter.doCommand(MainV2.comPort.BaseStream, "AT&T"); - - inter.doCommand(MainV2.comPort.BaseStream, "ATO"); - - MainV2.comPort.BaseStream.Close(); - - BUT_disconnect.Enabled = false; - BUT_connect.Enabled = true; - } - catch - { - } - } - private void timer1_Tick(object sender, EventArgs e) { var comPort = SikRadio.Config.comPort; @@ -137,40 +109,54 @@ private void timer1_Tick(object sender, EventArgs e) if (comPort.BytesToRead < 50) return; - var line = comPort.ReadLine(); + try + { + var line = comPort.ReadLine(); - /* -L/R RSSI: 12/0 L/R noise: 17/0 pkts: 0 txe=0 rxe=0 stx=0 srx=0 ecc=0/0 temp=61 dco=0 -L/R RSSI: 12/0 L/R noise: 16/0 pkts: 0 txe=0 rxe=0 stx=0 srx=0 ecc=0/0 temp=61 dco=0 - */ + /* + L/R RSSI: 12/0 L/R noise: 17/0 pkts: 0 txe=0 rxe=0 stx=0 srx=0 ecc=0/0 temp=61 dco=0 + L/R RSSI: 12/0 L/R noise: 16/0 pkts: 0 txe=0 rxe=0 stx=0 srx=0 ecc=0/0 temp=61 dco=0 + */ - var rssi = new Regex(@"RSSI: ([0-9]+)/([0-9]+)\s+L/R noise: ([0-9]+)/([0-9]+)"); + var rssi = new Regex(@"RSSI: ([0-9]+)/([0-9]+)\s+L/R noise: ([0-9]+)/([0-9]+)"); - var match = rssi.Match(line); + var match = rssi.Match(line); - if (match.Success) - { - var time = (Environment.TickCount - tickStart)/1000.0; + if (match.Success) + { + var time = (Environment.TickCount - tickStart) / 1000.0; - plotdatarssil.Add(time, double.Parse(match.Groups[1].Value)); - plotdatarssir.Add(time, double.Parse(match.Groups[2].Value)); - plotdatanoicel.Add(time, double.Parse(match.Groups[3].Value)); - plotdatanoicer.Add(time, double.Parse(match.Groups[4].Value)); + plotdatarssil.Add(time, double.Parse(match.Groups[1].Value)); + plotdatarssir.Add(time, double.Parse(match.Groups[2].Value)); + plotdatanoicel.Add(time, double.Parse(match.Groups[3].Value)); + plotdatanoicer.Add(time, double.Parse(match.Groups[4].Value)); - // Make sure the Y axis is rescaled to accommodate actual data - zedGraphControl1.AxisChange(); + // Make sure the Y axis is rescaled to accommodate actual data + zedGraphControl1.AxisChange(); - // Force a redraw + // Force a redraw - zedGraphControl1.Invalidate(); + zedGraphControl1.Invalidate(); - if (Terminal.sw != null) - { - Terminal.sw.Write(line); - Terminal.sw.Flush(); + if (Terminal.sw != null) + { + Terminal.sw.Write(line); + Terminal.sw.Flush(); + } } } + catch + { + } + } + } + + public string Header + { + get + { + return "RSSI"; } } } diff --git a/SikRadio/SikRadio.csproj b/SikRadio/SikRadio.csproj index 992a3f2f91..9ecc061046 100644 --- a/SikRadio/SikRadio.csproj +++ b/SikRadio/SikRadio.csproj @@ -118,6 +118,7 @@ Linked\XModem.cs + Form @@ -133,6 +134,11 @@ + + + + + UserControl @@ -228,6 +234,9 @@ Designer + + + diff --git a/SikRadio/Terminal.cs b/SikRadio/Terminal.cs index 4cdac91d33..2fc4128831 100644 --- a/SikRadio/Terminal.cs +++ b/SikRadio/Terminal.cs @@ -22,6 +22,11 @@ public Terminal() { InitializeComponent(); + SetupStreamWriter(); + } + + public static void SetupStreamWriter() + { if (sw == null) sw = new StreamWriter(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + "\\Terminal-" + DateTime.Now.ToString("yyyy-MM-dd HH-mm-ss") + ".txt"); } @@ -79,13 +84,22 @@ public void Connect() { if (!_RunRxThread) { - var Session = new RFD.RFD900.TSession(SikRadio.Config.comPort); - Session.PutIntoATCommandMode(); - Session.Dispose(); - - _RunRxThread = true; - _RxThread = new Thread(RxWorker); - _RxThread.Start(); + if (RFDLib.Utils.Retry(() => + { + var Session = new RFD.RFD900.TSession(SikRadio.Config.comPort, MainV2.comPort.BaseStream.BaudRate); + var Result = Session.PutIntoATCommandMode() == RFD.RFD900.TSession.TMode.AT_COMMAND; + Session.Dispose(); + return Result; + }, 3)) + { + _RunRxThread = true; + _RxThread = new Thread(RxWorker); + _RxThread.Start(); + } + else + { + MissionPlanner.MsgBox.CustomMessageBox.Show("Failed to enter AT command mode."); + } } } @@ -287,5 +301,13 @@ private void TXT_terminal_KeyPress(object sender, KeyPressEventArgs e) cmd.Append(e.KeyChar); } } + + public string Header + { + get + { + return "Terminal"; + } + } } } \ No newline at end of file