private void MainForm_FormClosed(object sender, FormClosedEventArgs e) { /* Failure to call Dispose will keep certain Windows handles * on, for example, shared memory, active for an interminate amount * of time before garbage collection. Its OK to fail if this process * is exiting: the OS will clean up. But if your process instances * multiple forms, then you had better do the Dispose's */ if (demodulator != null) { demodulator.Dispose(); } demodulator = null; if (wsjtExe != null) { wsjtExe.Dispose(); } wsjtExe = null; if (wsjtSharedMem != null) { wsjtSharedMem.Dispose(); } wsjtSharedMem = null; if (waveDevicePlayer != null) { waveDevicePlayer.Dispose(); } waveDevicePlayer = null; }
public void DisplaySpectrum(XDft.Demodulator demodulator) { if (IsDisposed) { return; } if (null == chartSpectrum) { return; } for (int j = 0; j < NUM_TO_AVERAGE; j++) { if (null == averagedSpectrum[j]) { averagedSpectrum[j] = new float[NUM_FREQUENCY_BINS]; } } float power = 0; /* wsjtx-2.0.1 implements its spectrum calculation in a non-reentrant ** way. The result is that ONLY one instance of XDft.Decoder ** can call GetSignalSpectrum else you won't get results that are ** of any use. You may switch from one instance to another only ** if you're willing to put up with an undefined length of time ** it takes for the new one to properly synchronize (a full 15sec ** cycle is enough, but before that, you don't know what you're getting.) */ uint samples = demodulator.GetSignalSpectrum(spectrum, ref power); for (int i = 0; i < NUM_FREQUENCY_BINS; i++) { averagedSpectrum[movingAverageIdx][i] = spectrum[i]; } movingAverageIdx += 1; if (movingAverageIdx >= NUM_TO_AVERAGE) { movingAverageIdx = 0; } numAveragedSpectrum += 1; if (numAveragedSpectrum >= NUM_TO_AVERAGE) { numAveragedSpectrum = NUM_TO_AVERAGE; } var series = chartSpectrum.Series[0]; series.Points.Clear(); for (int i = 0; i < 4096; i += 1) { double sample = 0; for (int j = 0; j < numAveragedSpectrum; j++) { sample += averagedSpectrum[j][i]; } series.Points.AddXY((5000 * i) / NUM_FREQUENCY_BINS, sample / numAveragedSpectrum); } labelPower.Text = power.ToString(); }
private XDft.RxSinkRepeater rxSinkRepeater; // need help repeating the RX audio to each decoder // have to know at construction time how many decoders public MultiDemodulatorWrapper(int instanceNumber, uint numDemodulators = 1) { if (numDemodulators > MAX_MULTIPROC) { numDemodulators = (uint)MAX_MULTIPROC; } if (numDemodulators == 0) { numDemodulators = 1; } demodulators = new XDft.Demodulator[numDemodulators]; wsjtSharedMems = new XDft.WsjtSharedMemory[numDemodulators]; wsjtExes = new XDft.WsjtExe[numDemodulators]; enabled = new bool[numDemodulators]; sharedMemoryKeys = new string[numDemodulators]; for (uint i = 0; i < numDemodulators; i++) { demodulators[i] = new XDft.Demodulator(); enabled[i] = true; // The first decoder gets the same subdirectory name as a single decoder string sharedMemoryKey = "DigiRite-" + instanceNumber.ToString(); if (i != 0) // subsequent ones get extra goop in their names { sharedMemoryKey += "-" + i.ToString(); } wsjtSharedMems[i] = new XDft.WsjtSharedMemory(sharedMemoryKey, false); if (!wsjtSharedMems[i].CreateWsjtSharedMem()) { throw new System.Exception("Failed to create Shared Memory from " + sharedMemoryKey); } // The subprocess itself is managed by the XDft wsjtExes[i] = new XDft.WsjtExe(); wsjtExes[i].AppDataName = sharedMemoryKey; sharedMemoryKeys[i] = sharedMemoryKey; if (!wsjtExes[i].CreateWsjtProcess(wsjtSharedMems[i])) { Dispose(); throw new System.Exception("Failed to launch wsjt exe"); } } multibandManager = new MultibandManagerOneDemod(demodulators); }
/* Note to programmers reading this for the first time: * Please do NOT be intimidated by all the comments in InitDemodulator! * They describe everything in gory detail. All you MUST know is this: * Instance these three objects from the XDft8 namespace: * Demodulator * WsjtSharedMemory * WsjtExe * * For the last two, choose the names in their properties carefully IF you * are going to run multiple instances of the Demodulator on the same PC. * * Set the Demodulator's callback delegate for decoded messages. * ...and do nothing more at initialization time. * * You also need a timer to call Demodulator.Clock(). See below. */ private void InitDemodulator(SetupForm sf) { // The objects implement IDisposable. If you fail to // dispose of one you quit using, its Windows resources // remain allocated until garbage collection. if (null != demodulator) { demodulator.Dispose(); } demodulator = null; if (null != wsjtSharedMem) { wsjtSharedMem.Dispose(); } wsjtSharedMem = null; if (null != wsjtExe) { wsjtExe.Dispose(); } wsjtExe = null; if (null != waveDevicePlayer) { waveDevicePlayer.Dispose(); } waveDevicePlayer = null; // The demodulator invokes the wsjtx decoder demodulator = new XDft.Demodulator(); // the names of its parameters are verbatim from the wsjt-x source code. // Don't ask this author what they mean. demodulator.nftx = 1500; demodulator.nfqso = 1500; demodulator.nQSOProgress = 5; demodulator.nzhsym = digiMode == XDft.DigiMode.DIGI_FT8 ? 50 : 18; mycall = sf.myCall.ToUpper(); demodulator.mycall = mycall; if (xmitForm != null) { xmitForm.mycall = mycall; } demodulator.digiMode = digiMode; // When the decoder finds an FT8 message, it calls us back... // ...on a foreign thread. Call BeginInvoke to get back on this one. See below. demodulator.DemodulatorResultCallback = new XDft.DemodResult(Decoded); // The wsjt-x code is run in a subprocess and we communicate with it using // cross-process shared memory. Such a construct requires the parent/sub-process // to agree on a name. That name must be unique to all the parent/sub's running // concurrently. That means that whatever name you give here must NOT // also be given to a copy of this programming running concurrently on the // same PC. For the test program, we leave it up to the user in the setup form. // that is a VERY bad idea for production...you have been warned. wsjtSharedMem = new XDft.WsjtSharedMemory(sf.sharedMemoryKey, false); if (!wsjtSharedMem.CreateWsjtSharedMem()) { MessageBox.Show("Failed to create Shared Memory from " + sf.sharedMemoryKey); return; } // The subprocess itself is managed by the XDft wsjtExe = new XDft.WsjtExe(); wsjtExe.AppDataName = sf.appDataName; if (!wsjtExe.CreateWsjtProcess(wsjtSharedMem)) { MessageBox.Show("Failed to launch wsjt exe"); demodulator.Dispose(); wsjtExe.Dispose(); wsjtExe = null; wsjtSharedMem.Dispose(); wsjtSharedMem = null; demodulator = null; return; } checkBoxEnableAP.Checked = demodulator.lft8apon; comboBoxnDepth.SelectedIndex = demodulator.ndepth - 1; #if DEBUG /* demonstrate how to use a native C++ dll to process ** incoming audio. Processing does not affect decoding. This is useful if you want to ** multi-instance XDft.Demodulator. Only one of XDft.Demodulator ** can call GetSignalSpectrum. The AudioProcessor interface can be instanced ** for multiple decodes concurrently and you can, for example, compute ** a spectrum in this processor .*/ IntPtr ip = XDft8RxAudioProcess.RxAudio.GetAudioProcessor(); demodulator.SetAudioSamplesCallback(new XDft.AudioCallback(SamplesCallback), 1000, 512, ip); #endif }