Click here to show toolbars of the Web Online Help System: show toolbars |
/*************************************************************************/ /* * File name: ActionTrigger.cpp * Location: See Matrox Example Launcher in the MIL Control Center * * * Synopsis: This example shows how to send a trigger to multiple cameras * at once using the GigE Vision� Action command. * The example then continues to show how to send a trigger to * multiple cameras at once using the GigE Vision� Scheduled * Action command. The Scheduled Action command relies on services * provided by the IEEE 1588 PTP (Precision Time Protocol). * * Copyright (C) Matrox Electronic Systems Ltd., 1992-2016. * All Rights Reserved */ #include <mil.h> #if M_MIL_USE_WINDOWS #include <windows.h> #endif #include <algorithm> #include "ActionDevice.h" #include "NetworkSegment.h" #define BUFFERING_SIZE_MAX 10 /* Function prototypes */ void DoAction(MIL_ID MilSystem); void DoScheduledAction(MIL_ID MilSystem); void ProgramActionDevices(const vector<ActionDevice>& Devices, MIL_INT64 DeviceKey, MIL_INT64 GroupKey, MIL_INT64 GroupMask); void ProgramActionContext(MIL_ID MilSystem, const vector<ActionDevice>& Devices, MIL_INT64 DeviceKey, MIL_INT64 GroupKey, MIL_INT64 GroupMask); bool ControlPrecisionTimeProtocolClocks(MIL_ID MilSystem, vector<ActionDevice>& Devices, bool Enable); void MFTYPE TriggerAction(TriggerFunctionArgument* Argument); void ClearAction(MIL_ID MilSystem); vector<MIL_DOUBLE> GetAcquisitionTimeMeasurments(MIL_INT BufferingSize, vector<ActionDevice>& Devices); /* Main function. */ int MosMain(void) { MIL_ID MilApplication; MIL_ID MilSystem ; MIL_INT SystemType ; /* Allocate defaults. */ MappAllocDefault(M_DEFAULT, &MilApplication, &MilSystem, M_NULL, M_NULL, M_NULL); /* Get information on the system we are using and print a welcome message to the console. */ MsysInquire(MilSystem, M_SYSTEM_TYPE, &SystemType); if(SystemType != M_SYSTEM_GIGE_VISION_TYPE) { /* Print error message. */ MosPrintf(MIL_TEXT("This example program can only be used with the Matrox Driver for ") MIL_TEXT("GigE Vision.\n")); MosPrintf(MIL_TEXT("Please ensure that the default system type is set accordingly in ") MIL_TEXT("MIL Config.\n")); MosPrintf(MIL_TEXT("-------------------------------------------------------------\n\n")); MosPrintf(MIL_TEXT("Press <enter> to quit.\n")); MosGetch(); MappFreeDefault(MilApplication, MilSystem, M_NULL, M_NULL, M_NULL); return 1; } /* Setup and trigger acquisition using the Action command. */ DoAction(MilSystem); MosPrintf(MIL_TEXT("\nPress <Enter> to continue.\n\n")); MosGetch(); #if M_MIL_USE_WINDOWS system("cls"); #endif /* Setup and trigger acquisition using the Scheduled Action command. */ DoScheduledAction(MilSystem); MosPrintf(MIL_TEXT("Press <Enter> to Quit.\n")); MosGetch(); /* Free defaults */ MappFreeDefault(MilApplication, MilSystem, M_NULL, M_NULL, M_NULL); return 0; } /* This routine detects GigE Vision compatible devices. It uses the Action command */ /* to simultaneously trigger the devices at once. */ void DoAction(MIL_ID MilSystem) { vector<NetworkSegment> NetworkSegments; vector<ActionDevice> ActionDevices; MosPrintf(MIL_TEXT("This example shows how to triggger an action signal across\n")); MosPrintf(MIL_TEXT("multiple GigE Vision devices\n\n")); MosPrintf(MIL_TEXT("Press <Enter> to continue.\n\n")); MosGetch(); MosPrintf(MIL_TEXT("Detecting connected GigE Vision device that support the Action command.\n\n")); /* Enumerate compatible devices. */ EnumNetworkSegments(MilSystem, M_GC_ACTION_SUPPORT, NetworkSegments); if (NetworkSegments.size() == 0) { MosPrintf(MIL_TEXT("\nNo compatible devices found\n")); return; } /* Print info relative to the devices found. */ MosPrintf(MIL_TEXT("\nCompatible GigE Vision devices found are:\n\n")); for (vector<NetworkSegment>::size_type i = 0; i < NetworkSegments.size(); i++) NetworkSegments[i].Print(eDevices); /* Inform user that the detected GigE Vision devices are physically connected to */ /* diffrent network segments. The Action signal packet will get replicated on these */ /* segments and will therefore reach the intended destination at diffrent times. */ if (NetworkSegments.size() > 1) { MosPrintf(MIL_TEXT("\nSome of the detected GigE Vision devices reside on diffrent network\n")); MosPrintf(MIL_TEXT("segments namely:\n\n")); for (vector<NetworkSegment>::size_type i = 0; i < NetworkSegments.size(); i++) NetworkSegments[i].Print(eAll); MosPrintf(MIL_TEXT("\nThe action signal packet will be replicated on these segments.\n")); MosPrintf(MIL_TEXT("Because of this the replicated action signal packet will arrive\n")); MosPrintf(MIL_TEXT("at diffrent times on diffrent network segments.")); } MosPrintf(MIL_TEXT("\nPress <Enter> to continue.\n\n")); MosGetch(); /* Reformat data structure... */ ActionDevices = ToActionDeviceVector(NetworkSegments); MosPrintf(MIL_TEXT("Programming Action keys into compatible devices.")); /* All devices that we want to trigger with this action must use the same device key */ MIL_INT DeviceKey = 0x56781234; /* Devices can be subdivided into groups; we will use the same group for all devices. */ MIL_INT GroupKey = 0x24; /* A device mask can be used to disable some group keys; we will enable all keys */ MIL_INT GroupMask = 0xFFFFFFFF; /* For an action to get triggerred the following conditions must be met: */ /* 1- The device must be allocated with MdigAlloc(). */ /* 2- The DeviceKey programmed here must match the DeviceKey sent by TriggerAction(). */ /* 3- The GroupKey programmed here must match the GroupKey sent by TriggerAction(). */ /* 4- The logical AND-wise comparison of the GroupMask programmed here with the GroupMask sent by TriggerAction() must be non-zero. */ ProgramActionDevices(ActionDevices, DeviceKey, GroupKey, GroupMask); ProgramActionContext(MilSystem, ActionDevices, DeviceKey, GroupKey, 0x1); /* Set an artificial trigger delay for demo purposes. We do this in case */ /* the connected device's frame rates are not the same. This will avoid */ /* overtriggering some devices. */ TriggerFunctionArgument Arg; Arg.MilSystem = MilSystem; Arg.ActionDelay = 100; /* Allocate resources required queue grabs. */ for (vector<ActionDevice>::size_type i = 0; i < ActionDevices.size(); i++) { ActionDevices[i].Allocate(BUFFERING_SIZE_MAX); if (i == 0) ActionDevices[i].StartAcquisition(BUFFERING_SIZE_MAX, TriggerAction, &Arg); else ActionDevices[i].StartAcquisition(BUFFERING_SIZE_MAX); } MosPrintf(MIL_TEXT("\nAction keys programmation complete.\n")); MosPrintf(MIL_TEXT("\nPress <Enter> to trigger actions.\n\n")); MosGetch(); /* Trigger the action. */ TriggerAction(&Arg); MosPrintf(MIL_TEXT("The initial action signal was sent.\n\n")); /* Wait for acquisition to complete. */ for (vector<ActionDevice>::size_type i = 0; i < ActionDevices.size(); i++) { ActionDevices[i].StopAcquisition(M_WAIT); MosPrintf(MIL_TEXT("%lld frames completed on %s %s\n\n"), (long long)ActionDevices[i].ProcessedImageCount, ActionDevices[i].Vendor.c_str(), ActionDevices[i].Model.c_str()); } /* Here inter-device jitter measurments is calculated using the GigE Vision driver's time stamp. */ /* The time stamp is read from the CPU for each device at the M_GRAB_FRAME_START event. */ /* This event is influenced by interrupt moderation of the host NIC (if enabled) and by the */ /* fact that multiple device streaming data to the same host NIC will have their streams */ /* serialized by devices such as Ethernet switches. */ /* Also diffrent settings used across devices (e.g. exposure time) can affect the jitter */ /* measurments. */ MosPrintf(MIL_TEXT("Inter-device jitter measurments:\n")); MosPrintf(MIL_TEXT("Note: the measurments can be affected by a multitude of factors such as:\n")); MosPrintf(MIL_TEXT("1- The use of diffrent exposure times across devices.\n")); MosPrintf(MIL_TEXT("2- The use interrupt moderation on the host Ethernet controller(s).\n\n")); /* Use acquisition timestamps gatherred during acquisition to calculate inter-frame delays between */ /* devices. */ vector<MIL_DOUBLE> TimeMeasurments = GetAcquisitionTimeMeasurments(BUFFERING_SIZE_MAX, ActionDevices); for (vector<MIL_DOUBLE>::size_type i = 0; i < TimeMeasurments.size(); i++) MosPrintf(MIL_TEXT("Frame: %-3d%-4.3f (usec)\n"), i + 1, TimeMeasurments[i] * 1e6); /* Free resources. */ for (vector<ActionDevice>::size_type i = 0; i < ActionDevices.size(); i++) ActionDevices[i].Free(); ClearAction(MilSystem); } /* This routine detects GigE Vision compatible devices. It uses the Scheduled Action command */ /* to simultaneously trigger the devices at once. */ void DoScheduledAction(MIL_ID MilSystem) { MIL_UINT32 Selection = 0; vector<NetworkSegment> NetworkSegments; vector<ActionDevice> ActionDevices; MosPrintf(MIL_TEXT("This example can also show how to triggger a scheduled action signal across\n")); MosPrintf(MIL_TEXT("multiple GigE Vision devices.\n\n")); MosPrintf(MIL_TEXT("The scheduled action signal allows for up to sub-microsecond synchronization\n")); MosPrintf(MIL_TEXT("between devices. It requires IEEE 1588 Precision Time Protocol (PTP) support\n")); MosPrintf(MIL_TEXT("from your GigE Vision device.\n\n")); MosPrintf(MIL_TEXT("Press <Enter> to continue.\n\n")); MosGetch(); MosPrintf(MIL_TEXT("Detecting network segments with compatible GigE Vision devices.\n\n")); /* Enumerate compatible devices. */ EnumNetworkSegments(MilSystem, M_GC_SCHEDULED_ACTION_SUPPORT + M_GC_IEEE_1588_SUPPORT, NetworkSegments); if (NetworkSegments.size() == 0) { MosPrintf(MIL_TEXT("\nNo compatible devices found\n")); return; } /* Print info relative to the devices found. */ MosPrintf(MIL_TEXT("\nCompatible GigE Vision devices found are:\n\n")); for (vector<NetworkSegment>::size_type i = 0; i < NetworkSegments.size(); i++) NetworkSegments[i].Print(eDevices); /* Inform user that the detected GigE Vision devices are physically connected to */ /* diffrent network segments. With IEEE 1588 PTP devices must reside on the same */ /* PTP domain otherwise they will not be able to synchronize their clocks. */ if (NetworkSegments.size() > 1) { MosPrintf(MIL_TEXT("\nSome of the detected GigE Vision devices reside on diffrent network\n")); MosPrintf(MIL_TEXT("segments namely:\n\n")); for (vector<NetworkSegment>::size_type i = 0; i < NetworkSegments.size(); i++) NetworkSegments[i].Print(eAll); MosPrintf(MIL_TEXT("\nIEEE 1588 PTP requires that devices reside on the same PTP domain for\n")); MosPrintf(MIL_TEXT("clock synchronization to occur. You must also ensure that the Ethernet\n")); MosPrintf(MIL_TEXT("bandwidth of all devices on a network segment does not exceed 125 MB/s\n")); MosPrintf(MIL_TEXT("(1 Gbps).\n\n")); MosPrintf(MIL_TEXT("Devices residing on diffrent PTP domains cannot be triggerred using\n")); MosPrintf(MIL_TEXT("the same action signal because they do not share a common clock.\n")); } MosPrintf(MIL_TEXT("\nPress <Enter> to continue.\n\n")); MosGetch(); /* If there is more than 1 netowrk segment with IEEE 1588 PTP compatible devices ask */ /* the use to select a single segment to use. This is because multiple segments might */ /* not be synchronized using the same PTP master clock. */ if (NetworkSegments.size() > 1) { MosPrintf(MIL_TEXT("Which network segment do you wish to use?\n\n")); for (vector<NetworkSegment>::size_type i = 0; i < NetworkSegments.size(); i++) { MosPrintf(MIL_TEXT("%3d"), i + 1); NetworkSegments[i].Print(eHostController); } MosPrintf(MIL_TEXT("\n")); do { #if M_MIL_USE_WINDOWS scanf_s("%d", &Selection); #else scanf("%d", (int *)&Selection); #endif if (Selection < 1 || Selection > NetworkSegments.size()) MosPrintf(MIL_TEXT("Invalid selection\n")); else break; } while (1); /* Adjust selection to index. */ Selection--; MosPrintf(MIL_TEXT("%s segment selected.\n\n"), NetworkSegments[Selection].HostControllerName.c_str()); } // Reformat devices to a simpler ActionDevices = ToActionDeviceVector(NetworkSegments, Selection); /* Enable IEEE1588 PTP on the selected devices. */ if (ControlPrecisionTimeProtocolClocks(MilSystem, ActionDevices, true)) { MosPrintf(MIL_TEXT("\nError: Precision Time Protocol initialization failed.\n")); MosGetch(); return; } MosPrintf(MIL_TEXT("Press <Enter> to continue.\n\n")); MosGetch(); MosPrintf(MIL_TEXT("Programming Action keys into compatible devices.")); /* All devices that we want to trigger with this action must use the same device key */ MIL_INT DeviceKey = 0x56781234; /* Devices can be subdivided into groups; we will use the same group for all devices. */ MIL_INT GroupKey = 0x24; /* A device mask can be used to disable some group keys; we will enable all keys */ MIL_INT GroupMask = 0xFFFFFFFF; /* For an action to get triggerred the following conditions must be met: */ /* 1- The device must be allocated with MdigAlloc(). */ /* 2- The DeviceKey programmed here must match the DeviceKey sent by TriggerAction(). */ /* 3- The GroupKey programmed here must match the GroupKey sent by TriggerAction(). */ /* 4- The logical AND-wise comparison of the GroupMask programmed here with the GroupMask sent by TriggerAction() must be non-zero. */ ProgramActionDevices(ActionDevices, DeviceKey, GroupKey, GroupMask); ProgramActionContext(MilSystem, ActionDevices, DeviceKey, GroupKey, 0x1); // Setup so scheduled action gets triggerred every 333 ms. TriggerFunctionArgument Arg; Arg.MilSystem = MilSystem; for (vector<ActionDevice>::size_type i = 0; i < ActionDevices.size(); i++) { ActionDevices[i].Allocate(BUFFERING_SIZE_MAX); if (i == 0) ActionDevices[i].StartAcquisition(BUFFERING_SIZE_MAX, TriggerAction, &Arg); else ActionDevices[i].StartAcquisition(BUFFERING_SIZE_MAX); } MosPrintf(MIL_TEXT("\nAction keys programmation complete.\n")); MosPrintf(MIL_TEXT("\nPress <Enter> to trigger an action.\n\n")); MosGetch(); /* Trigger the 1st action sometime in the future; say in 0.5 second from now. */ /* The other action signals will get triggerred from the MdigProcess hook callback of the first Device in the NetworkSegment. */ Arg.PtpDueTime = ActionDevices[0].GetDeviceTicks() + 0.5; TriggerAction(&Arg); MosPrintf(MIL_TEXT("The action signal was sent.\n\n")); for (vector<ActionDevice>::size_type i = 0; i < ActionDevices.size(); i++) { ActionDevices[i].StopAcquisition(M_WAIT); MosPrintf(MIL_TEXT("%lld frames completed on %s %s\n\n"), (long long)ActionDevices[i].ProcessedImageCount, ActionDevices[i].Vendor.c_str(), ActionDevices[i].Model.c_str()); } /* Here inter-device jitter measurments is calculated using the GigE Vision device time stamp. */ /* The time stamp generated by the GigE Vision device represents the time when the image was */ /* generated. With IEEE 1588 PTP enabled, the timestamps of all devices residing on the same PTP */ /* domain are synchronized. The synchronization precision acheivable is dependent upon multiple */ /* factors such as: */ /* 1- Network topology */ /* 2- The use of specialized devices (e.g. Ethernet swith) that uses special PTP clocks such as: */ /* - Transparent clocks */ /* - Boundary clocks */ /* Note that specialized devices are not required to use IEEE 1588 PTP */ MosPrintf(MIL_TEXT("Inter-device jitter measurments:\n\n")); MosPrintf(MIL_TEXT("Note: the measurments can be affected by multiple factors such as:\n")); MosPrintf(MIL_TEXT("1- The use of diffrent exposure times across devices.\n")); MosPrintf(MIL_TEXT("2- Network topology.\n")); MosPrintf(MIL_TEXT("3- The use (or lack of) IEEE1588 transparent clocks and/or IEEE1588 boundary clocks.\n")); vector<MIL_DOUBLE> TimeMeasurments = GetAcquisitionTimeMeasurments(BUFFERING_SIZE_MAX, ActionDevices); for (vector<MIL_DOUBLE>::size_type i = 0; i < TimeMeasurments.size(); i++) MosPrintf(MIL_TEXT("Frame: %-3d%-4.3f\n"), i + 1, TimeMeasurments[i] * 1e6); /* Disable IEEE1588 PTP on the selected devices. */ ControlPrecisionTimeProtocolClocks(MilSystem, ActionDevices, false); for (vector<ActionDevice>::size_type i = 0; i < ActionDevices.size(); i++) ActionDevices[i].Free(); ClearAction(MilSystem); } /* This routine is used to enable or disable IEEE 1588 PTP on GigE Vision devices. */ bool ControlPrecisionTimeProtocolClocks(MIL_ID MilSystem, vector<ActionDevice>& Devices, bool Enable) { bool Error = false; MIL_BOOL EnablePtp = Enable ? M_TRUE : M_FALSE; MIL_INT ReadyCount = 0, Count = 0; vector<milstring> Status(Devices.size(), MIL_TEXT("")); if (Enable) { MosPrintf(MIL_TEXT("Enabling IEEE 1588 Precision Time Protocol on GigE Vision devices\n\n")); for (vector<ActionDevice>::size_type i = 0; i < Devices.size(); i++) MdigControlFeature(Devices[i].MilDigitizer, M_FEATURE_VALUE, MIL_TEXT("GevIEEE1588"), M_TYPE_BOOLEAN, &EnablePtp); MosPrintf(MIL_TEXT("Waiting for GigE Vision Precision Time Protocol enabled devices to report\n")); MosPrintf(MIL_TEXT("readyness.\n\n")); do { ReadyCount = 0; MosSleep(50); Count++; MosPrintf(MIL_TEXT(".")); for (vector<ActionDevice>::size_type i = 0; i < Devices.size(); i++) { Status[i] = MdigInquireFeatureAsString(Devices[i].MilDigitizer, MIL_TEXT("GevIEEE1588Status")); if (Status[i] == MIL_TEXT("Master") || Status[i] == MIL_TEXT("Slave")) { ReadyCount++; Devices[i].IEEE1588Enabled = true; } } if (Count > 500) break; } while (ReadyCount < (MIL_INT)Devices.size()); MosPrintf(MIL_TEXT("\n\nIEEE 1588 Precision Time Protocol report:\n\n")); if (ReadyCount < (MIL_INT)Devices.size()) { Error = true; for (vector<ActionDevice>::size_type i = 0; i < Devices.size(); i++) { MosPrintf(MIL_TEXT("\t%s %s\n\tStatus: %s\n\n"), Devices[i].Vendor.c_str(), Devices[i].Model.c_str(), Status[i].c_str()); } MosPrintf(MIL_TEXT("Not all device status report ready.")); } else { milstring Accuracy; for (vector<ActionDevice>::size_type i = 0; i < Devices.size(); i++) { Accuracy = MdigInquireFeatureAsString(Devices[i].MilDigitizer, MIL_TEXT("GevIEEE1588ClockAccuracy")); MosPrintf(MIL_TEXT("\t%s %s\n\tStatus: %-12s Clock Accuracy: %-12s\n\n"), Devices[i].Vendor.c_str(), Devices[i].Model.c_str(), Status[i].c_str(), Accuracy.c_str()); } } } else { MosPrintf(MIL_TEXT("Disabling IEEE 1588 Precision Time Protocol on GigE Vision devices\n\n")); for (vector<ActionDevice>::size_type i = 0; i < Devices.size(); i++) MdigControlFeature(Devices[i].MilDigitizer, M_FEATURE_VALUE, MIL_TEXT("GevIEEE1588"), M_TYPE_BOOLEAN, &EnablePtp); } return Error; } /* This routine is used to program Action Keys and Masks into GigE Vision devices. These keys and masks are used */ /* by the devices when they receive the Action command. */ /* For an action to get triggerred the following conditions must be met: */ /* 1- The device must be allocated with MdigAlloc(). */ /* 2- The DeviceKey programmed here must match the DeviceKey sent by TriggerAction(). */ /* 3- The GroupKey programmed here must match the GroupKey sent by TriggerAction(). */ /* 4- The logical AND-wise comparison of the GroupMask programmed here with the GroupMask sent by TriggerAction() */ /* must be non-zero. */ void ProgramActionDevices(const vector<ActionDevice>& Devices, MIL_INT64 DeviceKey, MIL_INT64 GroupKey, MIL_INT64 GroupMask) { MIL_INT64 Selector = 0; for (vector<ActionDevice>::size_type i = 0; i < Devices.size(); i++) { /* Setup action keys and masks on each device. */ MdigControlFeature(Devices[i].MilDigitizer, M_FEATURE_VALUE, MIL_TEXT("ActionDeviceKey"), M_TYPE_INT64, &DeviceKey); MdigControlFeature(Devices[i].MilDigitizer, M_FEATURE_VALUE, MIL_TEXT("ActionSelector"), M_TYPE_INT64, &Devices[i].ActionNumber); MdigControlFeature(Devices[i].MilDigitizer, M_FEATURE_VALUE, MIL_TEXT("ActionGroupKey"), M_TYPE_INT64, &GroupKey); MdigControlFeature(Devices[i].MilDigitizer, M_FEATURE_VALUE, MIL_TEXT("ActionGroupMask"), M_TYPE_INT64, &GroupMask); /* Setup each device to trigger upon reception of an action signal. */ MdigControlFeature(Devices[i].MilDigitizer, M_FEATURE_VALUE, MIL_TEXT("TriggerSelector"), M_TYPE_STRING, MIL_TEXT("FrameStart")); MdigControlFeature(Devices[i].MilDigitizer, M_FEATURE_VALUE, MIL_TEXT("TriggerMode"), M_TYPE_STRING, MIL_TEXT("Off")); MdigControlFeature(Devices[i].MilDigitizer, M_FEATURE_VALUE, MIL_TEXT("TriggerSource"), M_TYPE_STRING, Devices[i].ActionName.c_str()); MdigControlFeature(Devices[i].MilDigitizer, M_FEATURE_VALUE, MIL_TEXT("TriggerMode"), M_TYPE_STRING, MIL_TEXT("On")); } } /* Setup a software context (M_GC_ACTION0 in this case) and store in it the action keys and masks that will be sent */ /* when MsysControl(MilSystem, M_GC_ACTION0 + M_GC_ACTION_EXECUTE, M_DEFAULT) is called. */ void ProgramActionContext(MIL_ID MilSystem, const vector<ActionDevice>& Devices, MIL_INT64 DeviceKey, MIL_INT64 GroupKey, MIL_INT64 GroupMask) { /* Use MIL M_GC_ACTION0 context to store the information */ /* Other contexts can be used to store other key groups. */ MsysControl(MilSystem, M_GC_ACTION0 + M_GC_ACTION_DEVICE_KEY, DeviceKey); MsysControl(MilSystem, M_GC_ACTION0 + M_GC_ACTION_GROUP_KEY, GroupKey); MsysControl(MilSystem, M_GC_ACTION0 + M_GC_ACTION_GROUP_MASK, GroupMask); for (vector<ActionDevice>::size_type i = 0; i < Devices.size(); i++) MsysControl(MilSystem, M_GC_ACTION0 + M_GC_ACTION_ADD_DEVICE, Devices[i].MilDigitizer); } /* Sends an Action, or a Scheduled Action command. If M_GC_ACTION_TIME is non zero then a Scheduled Action is sent. */ void MFTYPE TriggerAction(TriggerFunctionArgument* Argument) { if (Argument->PtpDueTime != 0.0) MsysControl(Argument->MilSystem, M_GC_ACTION0 + M_GC_ACTION_TIME, Argument->PtpDueTime); MsysControl(Argument->MilSystem, M_GC_ACTION0 + M_GC_ACTION_EXECUTE, M_DEFAULT); } /* Removes devices associated to this action context. */ void ClearAction(MIL_ID MilSystem) { MsysControl(MilSystem, M_GC_ACTION0 + M_GC_ACTION_CLEAR_DEVICES, M_DEFAULT); } /* Utility routine that computes the maximum jitter measurments across all devices. */ vector<MIL_DOUBLE> GetAcquisitionTimeMeasurments(MIL_INT BufferingSize, vector<ActionDevice>& Devices) { vector<MIL_DOUBLE> Jitter(BufferingSize, 0.0); MIL_DOUBLE Diff = 0.0; for (vector<ActionDevice>::size_type i = 0; i < Devices.size(); i++) { for (vector<ActionDevice>::size_type j = i; j < Devices.size(); j++) { if (i == j) continue; for (MIL_INT k = 0; k < BufferingSize; k++) { Diff = fabs(Devices[i].DeviceTimeStamps[k] - Devices[j].DeviceTimeStamps[k]); if (Jitter[k] < Diff) Jitter[k] = Diff; } } } return Jitter; }