// Copyright (c) 2010, Peter Smith
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// Redistributions of source code must retain the above copyright notice, this list
// of conditions and the following disclaimer.
//
// Redistributions in binary form must reproduce the above copyright notice, this
// list of conditions and the following disclaimer in the documentation and/or
// other materials provided with the distribution.
//
// Neither the name of the Author nor the names of its contributors may be used to
// endorse or promote products derived from this software without specific prior
// written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
namespace PS
{
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Media;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using OpenTK;
using OpenTK.Audio;
using OpenTK.Audio.OpenAL;
public partial class RadioTestSuite : Form
{
#region Fields
#region CODECS
private LumiSoft.Net.Media.Codec.Audio.PCMU uLaw = new LumiSoft.Net.Media.Codec.Audio.PCMU();
#endregion
#region Audio Out
//Create the audio out component
AudioOUT.PlayAudio playAudio;
#endregion
#region Audio In
//Create the audio in component
AudioIN.Microphone microphone;
//Thread used to poll microphone for data
Thread MicrophoneCapturedDataThread = null;
//Sample rate used to capture data per second
private int audioSamplingRate = 8000;
//Number of samples to collect (8000 samples per second / number of Samples = 64 ms of audio capture)
private int numberOfSamples = 512;
//Gain of microphone
private float microphoneGain = 4.0f;
//Used to export incoming voice data to wav format
MemoryStream memoryStreamAudioSaveBuffer = new MemoryStream();
#endregion
#region Protocol Data Unit
private const uint PDU_LENGTH_POSITION = 8;
private const uint PDU_TYPE_POSITION = 2;
private const uint PDU_VERSION_POSITION = 0;
private const double DIS_TIMESTAMP_SCALE = ((double)0x7fffffff / 3600.0); // (2^31 -1) Maximum value / seconds in hour
//Default to using endian type BIG
private DISnet.DataStreamUtilities.EndianTypes.Endian endianType = DISnet.DataStreamUtilities.EndianTypes.Endian.BIG;
PDUProcessorClass_Overrides pduReceive = new PDUProcessorClass_Overrides();
DIS1998net.SignalPdu signalPDU = new DIS1998net.SignalPdu();
DIS1998net.TransmitterPdu transmitterPDU = new DIS1998net.TransmitterPdu();
DIS1998net.EntityID entityID;
//Set intial state to off
DIS1998net.TransmitterPdu.TransmitStateEnum currentTransmittingState = DIS1998net.TransmitterPdu.TransmitStateEnum.TransmitterOff;
private List<SiteHostEntityRadioFrequency> siteHostEntityRadioFrequencyCollection = new List<SiteHostEntityRadioFrequency>();
#region PDU Heartbeat Timer
System.Timers.Timer timerTransmitter = new System.Timers.Timer(5000);
#endregion
#endregion
#region Radio Features
private ulong currentTransmittingFrequency = 0;
private string currentTransmitterID = "";
private bool isPTTActivated = false;
private Threaded.TList<FrequencySpeakerLocationTransmitterReceiverActiveClass> frequencySpeakerLocationTransmitterReceiverActive = new Threaded.TList<FrequencySpeakerLocationTransmitterReceiverActiveClass>();
private int numberOfRadios;
private bool isLoopBackAudioEnabled = false;
private enum ReceiveTransmitIndication { NotTransmitting, Transmitting, NotReceiving, Receiving };
#endregion
#region Socket
SocketCommunications.BroadCastReceive receiveBroadCast;
SocketCommunications.BroadCastSend sendData;
Thread receiveSocketData = null;
private string ipBroadCastAddressUsed;
private string ipUnicastAddressUsed;
private bool continueSendingMicrophoneData = false;
private bool continueReceivingSocketData = true;
private int defaultPort = 9737;
private ulong transmitterPacketsReceived = 0;
private ulong signalPacketsReceived = 0;
#endregion
#region Exercise Timers
private enum TypeTimeStamp { Absolute, Relative };
private Stopwatch exerciseTimer;
private DateTime ExerciseStartTime;
private Int64 ExerciseTimerStartValue = 0;
#endregion
object lockThis = new object();
public delegate void InvokeDelegate();
#endregion Fields
#region Constructors
public RadioTestSuite()
{
InitializeComponent();
if (SocketCommunications.SocketHelper.IPAddressesAvailable.Count == 0)
{
MessageBox.Show("No Network Interface Cards found");
return;
}
InitializeTimer();
InitializeRadioCommunications();
InitializeRadioReceiversTransmitters();
}
#endregion Constructors
#region Properties
public Threaded.TList<FrequencySpeakerLocationTransmitterReceiverActiveClass> FrequencySpeakerLocationTransmitterReceiverActive
{
get
{
return frequencySpeakerLocationTransmitterReceiverActive;
}
set
{
frequencySpeakerLocationTransmitterReceiverActive = value;
}
}
#endregion Properties
#region Methods
private void InitializeRadioCommunications()
{
//Initialize socket for sending BroadCast messages
sendData = new SocketCommunications.BroadCastSend();
//Grabbing first IP address in list
ipUnicastAddressUsed = SocketCommunications.SocketHelper.IPAddressesAvailable[0].IPAddress.ToString();
//Grabbing second IP address in list (note that this list contains both the unicast IP then the broadcast IP). Therefore the odd values
//will be the broadcast address based upon the previous unicast address in the list
ipBroadCastAddressUsed = SocketCommunications.SocketHelper.IPAddressesAvailable[0].BroadCastAddress.ToString();
//Initializing the socket to broadcast over the selected IP address and a default port number
sendData.StartBroadCastSend(ipBroadCastAddressUsed, defaultPort);
//Using the unicast IP address to assign to the entityID (each entity ID should be unique therefore IP address used, so do not run
//more than 1 instance of this app on the same machine
entityID = GetEntityID(ipUnicastAddressUsed);
//Assign default values to the Transmitter PDU, this PDU will be sent out to notify all other radios of its existence
transmitterPDU.EntityId = entityID;
transmitterPDU.RadioId = 1;
transmitterPDU.ExerciseID = 1;
//Start the heart beat, value set to 5 seconds
timerTransmitter.Elapsed += new System.Timers.ElapsedEventHandler(timerTransmitter_Elapsed);
timerTransmitter.Enabled = true;
//Use some default settings to send out the actual voice data
signalPDU.SampleRate = (uint)audioSamplingRate; //Default 8000 samples per second
signalPDU.Samples = (short)numberOfSamples; //number of samples in this packet 512 (with 8000 samples per second would give 64ms of audio)
//Set up what type of encoding was used
signalPDU.RadioSignalEncodingClass = DISnet.Enumerations.SignalPDUEnumerations.RadioSignalEncodingClassEnum.Encoded_Audio;
signalPDU.RadioSignalEncodingType = DISnet.Enumerations.SignalPDUEnumerations.RadioSignalEncodingTypeEnum.Eight_Bit_MuLaw;
//Set up socket to receive broadcast data from other radios
receiveBroadCast = new SocketCommunications.BroadCastReceive(ipBroadCastAddressUsed, defaultPort);
//Can be used to find out all of the capture devices on the system.
//IList<string> audioCaptureDevices = AudioCapture.AvailableDevices;
//foreach (var item in audioCaptureDevices)
//{
// string availableCaptureDevices = item.ToString();
//}
//Initialize the Microphone using the default microphone
StartMicrophone(audioSamplingRate, microphoneGain, AudioCapture.DefaultDevice, ALFormat.Mono16, numberOfSamples * 2);
//Start the process to send out microphone data through the socket
StartThreadSendingMicrophoneSoundToSignalPDU();
//Initialize the audio player
playAudio = new AudioOUT.PlayAudio();
toolStripStatusLabel_IPPort.Text = ipBroadCastAddressUsed + ":" + defaultPort.ToString();
//Start receiving transmitter and signal pdus from other radios
StartThreadToReceiveSocketData();
}
private void InitializeRadioReceiversTransmitters()
{
//Initialize number of radios
numberOfRadios = 0;
//This will count the number of radios found on the Form
FindControl(this.Controls);
}
/// <summary>
/// Starts the exercise Timer
/// </summary>
private void InitializeTimer()
{
exerciseTimer = new Stopwatch();
exerciseTimer.Reset();
exerciseTimer.Start();
ExerciseTimerStartValue = exerciseTimer.ElapsedTicks;
}
/// <summary>
/// Sets whether or not to loop the users recording back into their speaker
/// </summary>
private void checkBox_LoopBackAudioCheck_CheckedChanged(object sender, EventArgs e)
{
if (this.checkBox_LoopBackAudioCheck.Checked == true)
isLoopBackAudioEnabled = true;
else
isLoopBackAudioEnabled = false;
}
/// <summary>
/// Used to ensure that all started threads and processes are shut down correctly
/// </summary>
private void CompleteTest_FormClosing(object sender, FormClosingEventArgs e)
{
continueSendingMicrophoneData = false;
continueReceivingSocketData = false;
timerTransmitter.Enabled = false;
FrequencySpeakerLocationTransmitterReceiverActive.Clear();
siteHostEntityRadioFrequencyCollection.Clear();
if (microphone != null)
{
microphone.StopRecording();
}
if (playAudio != null)
{
playAudio.StopPlayBack();
playAudio = null;
}
if (receiveBroadCast != null)
{
receiveBroadCast.ShutDownSocket();
receiveBroadCast = null;
}
if (sendData != null)
{
sendData.ShutdownSocket();
sendData = null;
}
}
/// <summary>
/// Used to find a Radio control on the main form. Used to initialize the Radios found by providing
/// a unique radio tag which will be used when changing frequencys or speaker location.
/// </summary>
/// <param name="cntrl">Control collection from the Form</param>
private void FindControl(Control.ControlCollection cntrl)
{
foreach (Control c in cntrl)
{
if (c.GetType() == typeof(Radio))
{
FrequencySpeakerLocationTransmitterReceiverActiveClass tempRadioInfo = new FrequencySpeakerLocationTransmitterReceiverActiveClass();
Radio r = c as Radio;
r.Tag = numberOfRadios;
tempRadioInfo.UniqueID = r.Name;
frequencySpeakerLocationTransmitterReceiverActive.Add(tempRadioInfo);
numberOfRadios++;
}
else if (c.Controls.Count > 0)
FindControl(c.Controls);
}
}
/// <summary>
/// Determines if the frequency is suppose to be played back.
/// </summary>
/// <param name="frequency">Frequency to check</param>
/// <returns>True if frequency should be played back</returns>
private bool FrequencyOnPlayBackList(ulong frequency)
{
foreach (FrequencySpeakerLocationTransmitterReceiverActiveClass item in FrequencySpeakerLocationTransmitterReceiverActive)
{
//if freqency exists and it was selected to be a receiver or a transmitter than play back sound
if (item.Frequency == frequency && (item.ActiveReceiver == true || item.ActiveTransmitter == true))
return true;
}
return false;
}
/// <summary>
/// Method used to set unique Radio ID based upon the IP address of the system. Note that any method could be used as
/// long as the Radio is unique on the Network
/// </summary>
/// <param name="SimulationIPAddress">IP address of the system</param>
/// <returns>Entity ID</returns>
private DIS1998net.EntityID GetEntityID(string SimulationIPAddress)
{
string receiveAddress = SimulationIPAddress; //Just defaulting to first one in list
int lastOctectStart = receiveAddress.LastIndexOf('.') + 1;
string lastOctect = receiveAddress.Substring(lastOctectStart, (receiveAddress.Length - lastOctectStart));
return new DIS1998net.EntityID(1, 2, System.Convert.ToUInt16(lastOctect));
}
/// <summary>
/// Intializes the microphone
/// </summary>
/// <param name="samplingRate">Sample rate of recording</param>
/// <param name="microphoneGain">Software gain of microphone</param>
/// <param name="deviceCaptureName">System name of device used to capture audio</param>
/// <param name="format">Format of recorded data</param>
/// <param name="bufferSize">Size of buffer</param>
private void StartMicrophone(int samplingRate, float microphoneGain, string deviceCaptureName, ALFormat format, int bufferSize)
{
microphone = new AudioIN.Microphone(samplingRate, microphoneGain, deviceCaptureName, format, bufferSize);
if (microphone.isMicrophoneValid == false)
{
MessageBox.Show("ERROR SETTING UP MICROPHONE");
}
else
{
//Start accepting input from the microphone
microphone.StartRecording();
//Set flag to send captured data out to Signal PDU
continueSendingMicrophoneData = true;
}
}
/// <summary>
/// Starts thread that will continue to poll microphone and send data out over Signal PDU
/// </summary>
private void StartThreadSendingMicrophoneSoundToSignalPDU()
{
if (MicrophoneCapturedDataThread == null || MicrophoneCapturedDataThread.IsAlive == false)
{
continueSendingMicrophoneData = true;
MicrophoneCapturedDataThread = new Thread(OutputMicrophoneSoundToSignalPDU);
MicrophoneCapturedDataThread.Start();
}
}
/// <summary>
/// Microphone data will be sent out via a Signal PDU
/// </summary>
private void OutputMicrophoneSoundToSignalPDU()
{
while (continueSendingMicrophoneData)
{
if (microphone.MicrophoneData.Count > 0)
{
//Remove data from the Queue
byte[] raw = microphone.MicrophoneData.Dequeue();
//If PTT is true then data will be sent out on the PDU
if (isPTTActivated == true)
{
byte[] encodedData = uLaw.Encode(raw, 0, raw.Length);
raw = uLaw.Decode(encodedData, 0, encodedData.Length);
signalPDU.ExerciseID = transmitterPDU.ExerciseID;
signalPDU.EntityId = transmitterPDU.EntityId;
signalPDU.RadioId = transmitterPDU.RadioId;
signalPDU.Data = encodedData;
signalPDU.Samples = System.Convert.ToInt16(encodedData.Length);
signalPDU.Timestamp = TimeStamp(TypeTimeStamp.Absolute);
DISnet.DataStreamUtilities.DataOutputStream ds2 = new DISnet.DataStreamUtilities.DataOutputStream(endianType);
signalPDU.marshalAutoLengthSet(ds2);
sendData.BroadcastMessage(ds2.ConvertToBytes());
}
}
Thread.Sleep(1);
}
}
/// <summary>
/// Threaded method that will continue to poll socket for data
/// </summary>
private void RetreiveDataFromSocket()
{
byte pdu_type;
byte pdu_version;
//Continue to process data received until false
while (continueReceivingSocketData == true)
{
//Using queued collection to determine if data has arrived
if (receiveBroadCast.pduQueue.Count > 0)
{
//Process any PDUs (note that a collection was used as multiple pdu's can be sent in one packet)
List<byte[]> pdus = pduReceive.ProcessRawPDU(receiveBroadCast.pduQueue.Dequeue(), endianType);
int countPDU = 0;
int pduCount = pdus.Count;
while (countPDU < pduCount && continueReceivingSocketData == true)
{
byte[] objPdu = pdus[countPDU];
pdu_type = objPdu[PDU_TYPE_POSITION]; //get pdu type
pdu_version = objPdu[PDU_VERSION_POSITION];//what version (currently not processing anything but DIS 1998)
//Cast as radio pdu, as receive socket method will throw out all other types of PDUs
DIS1998net.RadioCommunicationsFamilyPdu pduReceived = pduReceive.ConvertByteArrayToPDU1998(pdu_type, objPdu, endianType) as DIS1998net.RadioCommunicationsFamilyPdu;
SiteHostEntityRadioFrequency siteHostEntityRadioFrequency = new SiteHostEntityRadioFrequency(pduReceived.EntityId.Site, pduReceived.EntityId.Application, pduReceived.EntityId.Entity, pduReceived.RadioId, 0);
if (pduReceived.EntityId != entityID || (pduReceived.EntityId == entityID && this.isLoopBackAudioEnabled == true))
{
//Transmitter which contains the frequency of the signal pdu
if (pdu_type == 25)
{
//Update transmitter packets received
UpdatedTransmitterPacketsReceived();
DIS1998net.TransmitterPdu transmitterPDU_Received = (DIS1998net.TransmitterPdu)pduReceived;
//Assign the frequency from the Transmitter PDU just received.
siteHostEntityRadioFrequency.frequency = transmitterPDU_Received.Frequency;
//Remove the frequency from the collection as it will be added back later if it is transmitting
siteHostEntityRadioFrequencyCollection.Remove(siteHostEntityRadioFrequencyCollection.Find(i => i.application == siteHostEntityRadioFrequency.application && i.entity == siteHostEntityRadioFrequency.entity && i.radioID == siteHostEntityRadioFrequency.radioID && i.site == siteHostEntityRadioFrequency.site));
//If transmitter is transmitting then add to collection
if (transmitterPDU_Received.TransmitStateEnumeration == DIS1998net.TransmitterPdu.TransmitStateEnum.TransmitterOnTransmitting)
{
siteHostEntityRadioFrequencyCollection.Add(siteHostEntityRadioFrequency);
}
foreach (FrequencySpeakerLocationTransmitterReceiverActiveClass item in FrequencySpeakerLocationTransmitterReceiverActive)
{
if (siteHostEntityRadioFrequencyCollection.Exists(i => i.frequency == item.Frequency) == true)
{
ChangeRadioRecievingColorIndicator(item.UniqueID,true);
}
else
ChangeRadioRecievingColorIndicator(item.UniqueID, false);
}
}
//is it a signal PDU?
if (pdu_type == 26)
{
//Update signal packets received
UpdatedSignalPacketsReceived();
DIS1998net.SignalPdu signalPDU = (DIS1998net.SignalPdu)pduReceived;
//Does the current signal pdu match one in the transmitter collection
if (siteHostEntityRadioFrequencyCollection.Exists(i => i.application == siteHostEntityRadioFrequency.application && i.entity == siteHostEntityRadioFrequency.entity && i.radioID == siteHostEntityRadioFrequency.radioID && i.site == siteHostEntityRadioFrequency.site) == true)
{
//Retrieve the saved frequency
SiteHostEntityRadioFrequency storedSitehostEntityRadioFrequency = siteHostEntityRadioFrequencyCollection.Find(i => i.application == siteHostEntityRadioFrequency.application && i.entity == siteHostEntityRadioFrequency.entity && i.radioID == siteHostEntityRadioFrequency.radioID && i.site == siteHostEntityRadioFrequency.site);
//Transmitter was transmitting at this frequency, need to check to see if it is one that we will playback
siteHostEntityRadioFrequency.frequency = storedSitehostEntityRadioFrequency.frequency;
if (FrequencyOnPlayBackList(storedSitehostEntityRadioFrequency.frequency) == true)
{
//Need to retrieve the speaker location that matches the frequency
FrequencySpeakerLocationTransmitterReceiverActiveClass retrievedFreqSpeakerTransmitterReceiver = FrequencySpeakerLocationTransmitterReceiverActive.Find(i => i.Frequency == storedSitehostEntityRadioFrequency.frequency);
//Decode the data, only implemented uLaw
byte[] unEncodedData = uLaw.Decode(((DIS1998net.SignalPdu)pduReceived).Data, 0, ((DIS1998net.SignalPdu)pduReceived).Data.Length);
//Used to save to a stream
//ms.Seek(ms.Length, SeekOrigin.Begin);
//ms.Write(encodedData, 0, encodedData.Length);
//Play back unencoded data
playAudio.PlayBackAudio(unEncodedData, ALFormat.Mono16, (int)signalPDU.SampleRate, retrievedFreqSpeakerTransmitterReceiver.SpeakerLocation);
}
}
}
}
countPDU++;
//Give GUI time to do things
Thread.Sleep(1);
}
}
//Give GUI time to do things
Thread.Sleep(1);
}
}
/// <summary>
/// Radio button pressed, pop up form to assign new frequency and speaker locations
/// </summary>
private void radio_RadioButtonPressed(object sender, EventArgs e)
{
//Get the Radio that was clicked
Radio radioButton = sender as Radio;
//Find the unique Tag
short radioID = System.Convert.ToInt16(radioButton.Tag);
//Get the Radio Name
string uniqueRadioIDName = radioButton.Name;
//Find out the current Frequency and speaker location
FrequencySpeakerLocationTransmitterReceiverActiveClass tempFreqSpeakerTransReceiverActive = FrequencySpeakerLocationTransmitterReceiverActive.Find(i => i.UniqueID == uniqueRadioIDName);
//Assign the current values to the Radio Form
RadioInterfaceForm radioInterfaceForm = new RadioInterfaceForm(tempFreqSpeakerTransReceiverActive);
//Center the form
radioInterfaceForm.Location = CenterModalForm(radioInterfaceForm.Size);
//Show the form
if (radioInterfaceForm.ShowDialog() == DialogResult.OK)
{
bool isFrequencyValid = true;
//Check to see frequency already assigned to another Radio
foreach (FrequencySpeakerLocationTransmitterReceiverActiveClass item in FrequencySpeakerLocationTransmitterReceiverActive)
{
if (item.UniqueID != uniqueRadioIDName)
{
if (radioInterfaceForm.FrequencyAndSpeakerLocation.Frequency == item.Frequency && radioInterfaceForm.Frequency != 0)
{
isFrequencyValid = false;
MessageBox.Show("Frequency already assigned");
break;
}
}
}
if (isFrequencyValid)
{
if (radioInterfaceForm.FrequencyAndSpeakerLocation.ActiveTransmitter == true)
{
//Disable from sending out heartbeat until new frequency assigned
this.timerTransmitter.Enabled = false;
foreach (FrequencySpeakerLocationTransmitterReceiverActiveClass item in FrequencySpeakerLocationTransmitterReceiverActive)
{
//Turn off all other transmitters, only one allowed
if (item.UniqueID != uniqueRadioIDName)
{
if (item.ActiveTransmitter == true)
{
//turn off the one that was a transmitter
item.ActiveTransmitter = false;
//Set it to just be a receiver
item.ActiveReceiver = true;
//Set correct color indicator
ChangeRadioColorIndicator(item.UniqueID, Radio.RadioType.Receiver);
}
}
}
//Assign new frequency to transmitter
currentTransmittingFrequency = radioInterfaceForm.FrequencyAndSpeakerLocation.Frequency;
//Change background shadow to indicate this is a transmitter
ChangeRadioColorIndicator(uniqueRadioIDName, Radio.RadioType.Transmitter);
//Assign the Unique Radio ID that is currently transmitting, used to change background color
currentTransmitterID = uniqueRadioIDName;
//Set current state to not transmitting
currentTransmittingState = DIS1998net.TransmitterPdu.TransmitStateEnum.TransmitterOnNotTransmitting;
//Transmit using new frequency then re-enable heartbeat
TransmitPDU();
this.timerTransmitter.Enabled = true;
}
else //ActiveTransmitter can be false, ActiveReceiver can be true or false
{
//If this radio was the transmitter, it has now been turned off so set currentTransmittingFrequency to 0
if (tempFreqSpeakerTransReceiverActive.ActiveTransmitter == true)
{
//Disable from sending out heartbeat until new frequency assigned
this.timerTransmitter.Enabled = false;
//Turn off Transmitter flag
tempFreqSpeakerTransReceiverActive.ActiveTransmitter = false;
//As there are no transmitters at this moment set to default freq 0
currentTransmittingFrequency = 0;
//Remove indication of active Transmitter from button
ChangeRadioColorIndicator(currentTransmitterID, Radio.RadioType.Neither);
//Remove TransmitterID
currentTransmitterID = "";
//Turn state to off, not sure if this is really valid as other radios can be receiving
currentTransmittingState = DIS1998net.TransmitterPdu.TransmitStateEnum.TransmitterOff;
//Transmit that we are off (not transmitting)
TransmitPDU();
//Re-enable heartbeat timer
this.timerTransmitter.Enabled = true;
}
if (radioInterfaceForm.FrequencyAndSpeakerLocation.ActiveReceiver == true)
{
//Set background shadow color to indicate Receiver
ChangeRadioColorIndicator(uniqueRadioIDName, Radio.RadioType.Receiver);
}
else
{
//Remove shadow as neither transmitter or receiver selected
ChangeRadioColorIndicator(uniqueRadioIDName, Radio.RadioType.Neither);
}
}
//Add the changes back in
tempFreqSpeakerTransReceiverActive = radioInterfaceForm.FrequencyAndSpeakerLocation;
FrequencySpeakerLocationTransmitterReceiverActive[radioID] = tempFreqSpeakerTransReceiverActive;
//If frequency was 0 then clear out text
radioButton.Text = radioInterfaceForm.Frequency > 0 ? radioInterfaceForm.Frequency.ToString() : "";
}
}
radioInterfaceForm.Close();
}
/// <summary>
/// Transmit audio over the frequency assigned to be the transmitter
/// </summary>
private void button_Transmit_MouseDown(object sender, MouseEventArgs e)
{
PTTActivated();
}
private void PTTActivated()
{
if (this.currentTransmittingFrequency != 0)
{
ChangeTransmitIndicator(this.currentTransmitterID, ReceiveTransmitIndication.Transmitting);
this.currentTransmittingState = DIS1998net.TransmitterPdu.TransmitStateEnum.TransmitterOnTransmitting;
this.TransmitPDU();
this.isPTTActivated = true;
}
}
/// <summary>
/// User released Transmit button, check to see if Transmit Indicators need to be switched
/// </summary>
private void button_Transmit_MouseUp(object sender, MouseEventArgs e)
{
PTTDeactivated();
}
private void PTTDeactivated()
{
if (this.currentTransmittingFrequency != 0)
{
ChangeTransmitIndicator(this.currentTransmitterID, ReceiveTransmitIndication.NotTransmitting);
this.isPTTActivated = false;
this.currentTransmittingState = DIS1998net.TransmitterPdu.TransmitStateEnum.TransmitterOnNotTransmitting;
this.TransmitPDU();
}
}
private void ChangeRadioColorIndicator(string UniqueRadioID, Radio.RadioType radioType)
{
Radio r = this.Controls.Find(UniqueRadioID, true)[0] as Radio;
if (this.InvokeRequired)
{
this.BeginInvoke(new MethodInvoker(delegate()
{
r.SetRadioShadowType(radioType);
}));
}
else
r.SetRadioShadowType(radioType);
}
private void ChangeRadioRecievingColorIndicator(string uniqueRadioID, bool receiving)
{
Radio r = this.Controls.Find(uniqueRadioID, true)[0] as Radio;
if (this.InvokeRequired)
{
this.BeginInvoke(new MethodInvoker(delegate()
{
r.ChangeRadioReceivingSignal(receiving);
}));
}
else
r.ChangeRadioReceivingSignal(receiving);
}
/// <summary>
/// Changes the indicator used to show that data is being transmitted
/// </summary>
/// <param name="UniqueRadioID">Unique ID of radio</param>
/// <param name="receiveTransmitIndication">Type of transmission being sent</param>
private void ChangeTransmitIndicator(string UniqueRadioID, ReceiveTransmitIndication receiveTransmitIndication)
{
Radio r = this.Controls.Find(UniqueRadioID, true)[0] as Radio;
switch (receiveTransmitIndication)
{
case ReceiveTransmitIndication.NotTransmitting:
if (this.InvokeRequired)
{
this.BeginInvoke(new MethodInvoker(delegate()
{
r.RadioStoppedTransmitting();
}));
}
else
r.RadioStoppedTransmitting();
break;
case ReceiveTransmitIndication.Transmitting:
if (this.InvokeRequired)
{
this.BeginInvoke(new MethodInvoker(delegate()
{
r.RadioTransmitting();
}));
}
else
r.RadioTransmitting();
break;
default:
break;
}
}
/// <summary>
/// Used to update transmitter packet count
/// </summary>
private void UpdatedTransmitterPacketsReceived()
{
transmitterPacketsReceived++;
if (this.InvokeRequired)
{
this.BeginInvoke(new MethodInvoker(delegate()
{
toolStripStatusLabel_TransmitterPackets.Text = transmitterPacketsReceived.ToString();
}));
}
else
toolStripStatusLabel_TransmitterPackets.Text = transmitterPacketsReceived.ToString();
}
/// <summary>
/// Used to update signal packet count
/// </summary>
private void UpdatedSignalPacketsReceived()
{
signalPacketsReceived++;
if (this.InvokeRequired)
{
this.BeginInvoke(new MethodInvoker(delegate()
{
toolStripStatusLabel_SignalPakcets.Text = signalPacketsReceived.ToString();
}));
}
else
toolStripStatusLabel_SignalPakcets.Text = signalPacketsReceived.ToString();
}
/// <summary>
/// Center a form
/// </summary>
/// <param name="childForm">Form to be centered</param>
/// <returns>X, Y coordinates</returns>
private Point CenterModalForm(Size childForm)
{
Point newLocation = new Point();
int xLoc = this.Location.X;
int yLoc = this.Location.Y;
Size s = this.Size;
newLocation.X = (xLoc + s.Width / 2) - childForm.Width / 2;
newLocation.Y = (yLoc + s.Height / 2) - childForm.Height / 2;
return newLocation;
}
/// <summary>
/// Type of typestamp to apply to outgoing PDUs
/// </summary>
/// <param name="typeTimeStamp">Type of timestamp to use</param>
/// <returns>Integer value of the resulting timestamp</returns>
private UInt32 TimeStamp(TypeTimeStamp typeTimeStamp)
{
UInt32 timeStamp = 0;
//ExerciseStartTime
switch (typeTimeStamp)
{
case TypeTimeStamp.Absolute:
timeStamp = AbsoluteTimeStamp(exerciseTimer.ElapsedTicks);
break;
case TypeTimeStamp.Relative:
timeStamp = RelativeTimeStamp(exerciseTimer.ElapsedTicks);
break;
default:
break;
}
return timeStamp;
}
/// <summary>
/// Creates an Absolute Time Stamp
/// </summary>
/// <param name="timeTicks">Number of ticks based upon the start of the exercise in UTC</param>
/// Note: Having read the IEEE 1278.1 standard I am not sure this is implemented correctly</param>
/// <returns>Integer value of the resulting timestamp</returns>
private UInt32 AbsoluteTimeStamp(long timeTicks)
{
UInt32 timeStamp = 0;
//Number of seconds past the hour according to the IEEE standard for absolute stamp
DateTime time = this.ExerciseStartTime.AddTicks(timeTicks - ExerciseTimerStartValue);
//Number of seconds past the hour according to the IEEE standard for relative stamp
double totalSeconds = time.Minute * 60.0 + time.Second + time.Millisecond / 1000.0;
timeStamp = System.Convert.ToUInt32(totalSeconds * DIS_TIMESTAMP_SCALE);
timeStamp = (timeStamp << 1) | 1;//Shifting by 1 bit then adding 1 in the LSB represents Absolute
return timeStamp;
}
/// <summary>
/// Create a relative Time Stamp
/// </summary>
/// <param name="timeTicks">Number of ticks based upon the start of the exercise that all participants agree upon.
/// Note: Having read the IEEE 1278.1 standard I am not sure this is implemented correctly</param>
/// <returns></returns>
private UInt32 RelativeTimeStamp(long timeTicks)
{
UInt32 timeStamp = 0;
//TimeSpan time = TimeSpan.FromTicks(timeTicks);
//Int64 timeElapseInTenthsOfMilliseconds = (timeTicks * 10000) / timer.Frequency;
DateTime time = this.ExerciseStartTime.AddTicks(timeTicks - ExerciseTimerStartValue);
//Number of seconds past the hour according to the IEEE standard for relative stamp
double totalSeconds = time.Minute * 60.0 + time.Second + time.Millisecond / 1000.0;
timeStamp = System.Convert.ToUInt32(totalSeconds * DIS_TIMESTAMP_SCALE * 2.0); //Multiplying by 2 to shift bits to the left a 0 in the LSB represents Relative
return timeStamp;
}
/// <summary>
/// Used to save audio data into a wave compatible format
/// </summary>
/// <param name="channels">Number of channels (mono or stereo)</param>
/// <param name="samplerate">Sample rate of audio data</param>
/// <param name="BitsPerSample">Number of bits per sample</param>
private void RIFFHeader(short channels, int samplerate, short BitsPerSample)
{
//For the default state: RIFFHeader(1, 8000, 16);
int fileSize = 36 + (int)memoryStreamAudioSaveBuffer.Length;
FileStream fs = new FileStream("AudioReceived.wav", FileMode.Create);
BinaryWriter bw = new BinaryWriter(fs);
fs.Position = 0;
bw.Write(new char[4] { 'R', 'I', 'F', 'F' });
bw.Write(fileSize);
bw.Write(new char[8] { 'W', 'A', 'V', 'E', 'f', 'm', 't', ' ' });
bw.Write((int)16);
bw.Write((short)1);
bw.Write(channels);
bw.Write(samplerate);
bw.Write((int)(samplerate * ((BitsPerSample * channels) / 8)));
bw.Write((short)((BitsPerSample * channels) / 8));
bw.Write(BitsPerSample);
bw.Write(new char[4] { 'd', 'a', 't', 'a' });
bw.Write((int)memoryStreamAudioSaveBuffer.Length);
bw.Write(memoryStreamAudioSaveBuffer.ToArray());
bw.Close();
fs.Close();
}
/// <summary>
/// Start thread that will receive socket data
/// </summary>
private void StartThreadToReceiveSocketData()
{
//allow processing of socket data
continueReceivingSocketData = true;
//Start the thread
receiveSocketData = new Thread(RetreiveDataFromSocket);
receiveSocketData.Start();
}
/// <summary>
/// Heart beat timer used to send updates about the transmitter on this Radio
/// </summary>
private void timerTransmitter_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
TransmitPDU();
}
/// <summary>
/// Send out a Transmitter PDU
/// </summary>
private void TransmitPDU()
{
transmitterPDU.Frequency = currentTransmittingFrequency;
transmitterPDU.TransmitStateEnumeration = currentTransmittingState;
transmitterPDU.Timestamp = TimeStamp(TypeTimeStamp.Absolute);
DISnet.DataStreamUtilities.DataOutputStream ds = new DISnet.DataStreamUtilities.DataOutputStream(endianType);
transmitterPDU.marshalAutoLengthSet(ds);
this.sendData.BroadcastMessage(ds.ConvertToBytes());
}
#endregion Methods
}
}