Skip to content

Commit

Permalink
Merge pull request #486 from NordicSemiconductor/feat/add-digital-ch-…
Browse files Browse the repository at this point in the history
…triggers

Feat/add digital ch triggers
  • Loading branch information
tsvetelinpetrov authored Feb 4, 2025
2 parents 08291f9 + 272bb4d commit a9af19b
Show file tree
Hide file tree
Showing 10 changed files with 400 additions and 101 deletions.
1 change: 1 addition & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

### Added

- Digital channels can now be configured as triggers in the `Scope` pane.
- Option for cooshing a trigger bias level.
- Option for choosing the triggering edge.

Expand Down
107 changes: 89 additions & 18 deletions src/actions/deviceActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,19 +64,24 @@ import {
import { updateGainsAction } from '../slices/gainsSlice';
import {
clearProgress,
DigitalChannelTriggerStatesEnum,
getTriggerBias,
getTriggerRecordingLength,
resetTriggerOrigin,
setProgress,
setTriggerActive,
setTriggerOrigin,
TriggerEdge,
} from '../slices/triggerSlice';
import { updateRegulator as updateRegulatorAction } from '../slices/voltageRegulatorSlice';
import { convertBits16 } from '../utils/bitConversion';
import { convertTimeToSeconds } from '../utils/duration';
import { isDiskFull } from '../utils/fileUtils';
import { isDataLoggerPane } from '../utils/panes';
import { setSpikeFilter as persistSpikeFilter } from '../utils/persistentStore';
import {
digitalChannelStateTupleOf8,
setSpikeFilter as persistSpikeFilter,
} from '../utils/persistentStore';

let device: null | SerialDevice = null;
let updateRequestInterval: NodeJS.Timeout | undefined;
Expand Down Expand Up @@ -210,6 +215,68 @@ const initGains = (): AppThunk<RootState, Promise<void>> => async dispatch => {
);
};

function checkDigitalTriggerValidity(
unsignedBits: number,
previousUnsignedBits: number,
channelTriggerStatuses: digitalChannelStateTupleOf8
): boolean {
const channelTriggerStatusesReversed = [
...channelTriggerStatuses,
].reverse();

const doNotCareMask = Number.parseInt(
channelTriggerStatusesReversed
.map(status =>
status === DigitalChannelTriggerStatesEnum.DoNotCare ? '0' : '1'
)
.join(''),
2
);

const validMask = Number.parseInt(
channelTriggerStatusesReversed
.map(status =>
status === DigitalChannelTriggerStatesEnum.DoNotCare
? '0'
: status
)
.join(''),
2
);

const isTriggerValid = (bits: number) =>
((bits & doNotCareMask) ^ validMask) === 0;

return (
!isTriggerValid(previousUnsignedBits) && isTriggerValid(unsignedBits)
);
}

function checkAnalogTriggerValidity(
cappedValue: number,
prevCappedValue: number | undefined,
triggerLevel: number,
triggerEdge: TriggerEdge
): boolean {
const isRaisingEdge = triggerEdge === 'Raising Edge';
const isLoweringEdge = triggerEdge === 'Lowering Edge';

let validTriggerValue = false;

if (isRaisingEdge) {
validTriggerValue =
prevCappedValue != null &&
prevCappedValue < triggerLevel &&
cappedValue >= triggerLevel;
} else if (isLoweringEdge) {
validTriggerValue =
prevCappedValue != null &&
prevCappedValue > triggerLevel &&
cappedValue <= triggerLevel;
}
return validTriggerValue;
}

export const open =
(deviceInfo: Device): AppThunk<RootState, Promise<void>> =>
async (dispatch, getState) => {
Expand All @@ -223,6 +290,7 @@ export const open =
let prevValue = 0;
let prevCappedValue: number | undefined;
let prevBits = 0;
let prevUnsignedBits = 0;
let nbSamples = 0;
let nbSamplesTotal = 0;

Expand All @@ -243,6 +311,9 @@ export const open =
cappedValue = 0;
}

const channelTriggerStatuses =
state.app.trigger.digitalChannelsTriggersStates;
const unsignedBits = bits !== undefined ? bits & 0xff : 0;
const b16 = convertBits16(bits!);

if (samplingRunning && sampleFreq < maxSampleFreq) {
Expand All @@ -267,25 +338,25 @@ export const open =
prevBits = 0;

if (getRecordingMode(state) === 'Scope') {
const isRaisingEdge = state.app.trigger.edge === 'Raising Edge';
const isLoweringEdge =
state.app.trigger.edge === 'Lowering Edge';

let validTriggerValue = false;

if (isRaisingEdge) {
validTriggerValue =
prevCappedValue != null &&
prevCappedValue < state.app.trigger.level &&
cappedValue >= state.app.trigger.level;
} else if (isLoweringEdge) {
validTriggerValue =
prevCappedValue != null &&
prevCappedValue > state.app.trigger.level &&
cappedValue <= state.app.trigger.level;
}
const triggerCategory = state.app.trigger.category;

const validTriggerValue =
triggerCategory === 'Analog'
? checkAnalogTriggerValidity(
cappedValue,
prevCappedValue,
state.app.trigger.level,
state.app.trigger.edge
)
: prevUnsignedBits !== unsignedBits &&
checkDigitalTriggerValidity(
unsignedBits,
prevUnsignedBits,
channelTriggerStatuses
);

prevCappedValue = cappedValue;
prevUnsignedBits = unsignedBits;

if (!DataManager().isInSync()) {
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,11 @@ import {
import { appState } from '../../slices/appSlice';
import { getRecordingMode } from '../../slices/chartSlice';
import {
getTriggerBias,
getTriggerEdge,
getTriggerRecordingLength,
getTriggerType,
getTriggerValue,
setTriggerBias,
setTriggerEdge,
setTriggerLevel,
setTriggerRecordingLength,
setTriggerType,
TriggerEdgeValues,
TriggerTypeValues,
} from '../../slices/triggerSlice';

const CurrentUnitValues = ['mA', '\u00B5A'] as const;
Expand All @@ -50,15 +43,9 @@ const convertToMicroAmps = (unit: CurrentUnit, value: number) => {
}
};

const calculateBiasTime = (recordingLength: number, bias: number) =>
Number((recordingLength * (bias / 100)).toFixed(2));

export default () => {
const dispatch = useDispatch();
const recordingLength = useSelector(getTriggerRecordingLength);
const triggerBias = useSelector(getTriggerBias);
const triggerValue = useSelector(getTriggerValue);
const triggerType = useSelector(getTriggerType);
const triggerEdge = useSelector(getTriggerEdge);
const { samplingRunning } = useSelector(appState);
const dataLoggerActive =
Expand All @@ -73,12 +60,6 @@ export default () => {

const [internalTriggerValue, setInternalTriggerValue] =
useState(triggerValue);
const [internalTriggerLength, setInternalTriggerLength] =
useState(recordingLength);
const [triggerBiasValue, setTriggerBiasValue] = useState(triggerBias);
const [computedBias, setComputedBias] = useState(
calculateBiasTime(internalTriggerLength, triggerBias)
);

useEffect(() => {
if (triggerValue > 1000) {
Expand All @@ -90,62 +71,8 @@ export default () => {
}
}, [triggerValue]);

useEffect(() => {
setInternalTriggerLength(recordingLength);
}, [recordingLength]);

useEffect(() => {
setTriggerBiasValue(triggerBias);
}, [triggerBias]);

return (
<>
<NumberInput
range={{
min: 1,
max: 1000,
decimals: 2,
step: 0.01,
}}
title="Duration of trigger window"
value={internalTriggerLength}
onChange={setInternalTriggerLength}
onChangeComplete={(value: number) => {
dispatch(setTriggerRecordingLength(value));
setComputedBias(calculateBiasTime(value, triggerBias));
}}
unit="ms"
label="Length"
disabled={dataLoggerActive}
showSlider
/>
<div className="tw-flex tw-flex-col tw-gap-2">
<NumberInput
range={{
min: 0,
max: 100,
decimals: 0,
step: 1,
}}
title='Trigger bias from "Start of trigger window"'
value={triggerBiasValue}
onChange={setTriggerBiasValue}
onChangeComplete={(value: number) => {
dispatch(setTriggerBias(value));
setComputedBias(
calculateBiasTime(internalTriggerLength, value)
);
}}
unit="%"
label="Bias"
disabled={dataLoggerActive}
showSlider
/>
<div className="tw-text-sm tw-text-gray-500">
Computed bias: {computedBias} ms
</div>
</div>

<NumberInput
range={{
min: getMin(levelUnit),
Expand Down Expand Up @@ -179,12 +106,6 @@ export default () => {
disabled={dataLoggerActive}
showSlider
/>
<StateSelector
items={[...TriggerTypeValues]}
onSelect={m => dispatch(setTriggerType(TriggerTypeValues[m]))}
selectedItem={triggerType}
disabled={samplingRunning}
/>
<StateSelector
items={[...TriggerEdgeValues]}
onSelect={m => {
Expand Down
82 changes: 82 additions & 0 deletions src/components/SidePanel/DigitalTriggerSettings.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
* Copyright (c) 2015 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: LicenseRef-Nordic-4-Clause
*/

import React from 'react';
import { useDispatch, useSelector } from 'react-redux';
import {
Dropdown,
DropdownItem,
} from '@nordicsemiconductor/pc-nrfconnect-shared';

import {
DigitalChannelTriggerStatesEnum,
getDigitalChannelsTriggersStates,
setDigitalChannelsTriggersStates,
} from '../../slices/triggerSlice';
import { digitalChannelStateTupleOf8 } from '../../utils/persistentStore';

const dropdownItems: DropdownItem[] = [
{
value: DigitalChannelTriggerStatesEnum.Active,
label: 'Active',
},
{
value: DigitalChannelTriggerStatesEnum.Inactive,
label: 'Inactive',
},
{
value: DigitalChannelTriggerStatesEnum.DoNotCare,
label: "Don't care",
},
];

export default () => {
const dispatch = useDispatch();
const digitalChannelTriggerStates = useSelector(
getDigitalChannelsTriggersStates
);

const handleDigitalChannelsTriggerStateChange = (
index: number,
state: DigitalChannelTriggerStatesEnum
) => {
const newStates = [
...digitalChannelTriggerStates,
] as digitalChannelStateTupleOf8;
newStates[index] = state;
dispatch(
setDigitalChannelsTriggersStates({
digitalChannelsTriggers: newStates,
})
);
};

return (
<div className="tw-flex tw-flex-col tw-gap-3">
{digitalChannelTriggerStates.map((state, index) => (
<div
key={`d-trigger-${index + 1}`}
className="tw-flex tw-flex-row tw-gap-3"
>
<div>{`Digital channel ${index}:`}</div>
<Dropdown
onSelect={value => {
handleDigitalChannelsTriggerStateChange(
index,
value.value as DigitalChannelTriggerStatesEnum
);
}}
items={dropdownItems}
selectedItem={
dropdownItems.find(item => item.value === state) ??
dropdownItems[0]
}
/>
</div>
))}
</div>
);
};
Loading

0 comments on commit a9af19b

Please sign in to comment.