Skip to content

Commit 5d87842

Browse files
authored
feat: added Logic Analyzer (#2775)
1 parent b53e140 commit 5d87842

11 files changed

+1606
-21
lines changed

lib/communication/digitalChannel/digital_channel.dart

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ class DigitalChannel {
77
static const int everyFourthRisingEdge = 4;
88
static const int everyRisingEdge = 3;
99
static const int everyFallingEdge = 2;
10-
static const int captureDelay = 2;
1110
static List<String> digitalChannelNames = [
1211
'LA1',
1312
'LA2',
@@ -59,7 +58,7 @@ class DigitalChannel {
5958
final int s = initialStates[channelName]!;
6059
initialState = s == 1;
6160
}
62-
this.timestamps.setRange(0, timestamps.length - 1, timestamps);
61+
this.timestamps.setRange(0, timestamps.length, timestamps);
6362
dLength = timestamps.length;
6463
double factor = 64;
6564
List<double> diff = [];
@@ -69,12 +68,12 @@ class DigitalChannel {
6968
for (int i = 0; i < diff.length; i++) {
7069
if (diff[i] < 0) {
7170
for (int j = i + 1; j < this.timestamps.length; j++) {
72-
this.timestamps[j] += (1 << 16) - 1;
71+
this.timestamps[j] += ((1 << 16) - 1);
7372
}
7473
}
7574
}
7675
for (int i = 0; i < this.timestamps.length; i++) {
77-
this.timestamps[i] = (this.timestamps[i] + (i * captureDelay)) / factor;
76+
this.timestamps[i] = (this.timestamps[i]) / factor;
7877
}
7978
if (dLength > 0) {
8079
maxT = this.timestamps[this.timestamps.length - 1];
@@ -98,8 +97,8 @@ class DigitalChannel {
9897
} else if (mode == everyEdge) {
9998
xAxis[0] = 0;
10099
yAxis[0] = state.toDouble();
101-
int length = 0;
102-
for (int i = 1, j = 1; i < dLength; i++, j++) {
100+
int j = 1;
101+
for (int i = 1; i < dLength; i++, j++) {
103102
xAxis[j] = timestamps[i];
104103
yAxis[j] = state.toDouble();
105104
if (state == high) {
@@ -110,14 +109,13 @@ class DigitalChannel {
110109
j++;
111110
xAxis[j] = timestamps[i];
112111
yAxis[j] = state.toDouble();
113-
length = j;
114112
}
115-
plotLength = length;
113+
plotLength = j;
116114
} else if (mode == everyFallingEdge) {
117115
xAxis[0] = 0;
118116
yAxis[0] = high.toDouble();
119-
int length = 0;
120-
for (int i = 1, j = 1; i < dLength; i++, j++) {
117+
int j = 1;
118+
for (int i = 1; i < dLength; i++, j++) {
121119
xAxis[j] = timestamps[i];
122120
yAxis[j] = high.toDouble();
123121
j++;
@@ -126,16 +124,15 @@ class DigitalChannel {
126124
j++;
127125
xAxis[j] = timestamps[i];
128126
yAxis[j] = high.toDouble();
129-
length = j;
130127
}
131128
state = high;
132-
plotLength = length;
129+
plotLength = j;
133130
} else if (mode == everyRisingEdge ||
134131
mode == everyFourthRisingEdge ||
135132
mode == everySixteenthRisingEdge) {
136133
xAxis[0] = 0;
137134
yAxis[0] = low.toDouble();
138-
int length = 0;
135+
int j = 1;
139136
for (int i = 1, j = 1; i < dLength; i++, j++) {
140137
xAxis[j] = timestamps[i];
141138
yAxis[j] = low.toDouble();
@@ -145,10 +142,9 @@ class DigitalChannel {
145142
j++;
146143
xAxis[j] = timestamps[i];
147144
yAxis[j] = low.toDouble();
148-
length = j;
149145
}
150146
state = low;
151-
plotLength = length;
147+
plotLength = j;
152148
}
153149
}
154150

lib/communication/science_lab.dart

Lines changed: 142 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,10 @@ class ScienceLab {
439439
}
440440
}
441441

442+
DigitalChannel getDigitalChannel(int i) {
443+
return dChannels[i];
444+
}
445+
442446
int? calculateDigitalChannel(String name) {
443447
if (DigitalChannel.digitalChannelNames.contains(name)) {
444448
return DigitalChannel.digitalChannelNames.indexOf(name);
@@ -507,8 +511,44 @@ class ScienceLab {
507511
return null;
508512
}
509513

514+
Future<bool> fetchLAChannel(int channelNumber,
515+
LinkedHashMap<String, int> initialStates, int channels) async {
516+
DigitalChannel dChan = dChannels[channelNumber];
517+
518+
LinkedHashMap<String, int> tempMap = LinkedHashMap<String, int>();
519+
tempMap['LA1'] = initialStates['LA1']!;
520+
tempMap['LA2'] = initialStates['LA2']!;
521+
tempMap['LA3'] = initialStates['LA3']!;
522+
tempMap['LA4'] = initialStates['LA4']!;
523+
tempMap['RES'] = initialStates['RES']!;
524+
525+
int i = 0;
526+
for (MapEntry<String, int> entry in initialStates.entries) {
527+
if (dChan.channelNumber == i) {
528+
i = entry.value;
529+
break;
530+
}
531+
i++;
532+
}
533+
List<int>? temp =
534+
await fetchIntDataFromLA(i, dChan.channelNumber + 1, channels);
535+
List<double> data = List.filled(temp!.length - 1, 0.0);
536+
if (temp[0] == 1) {
537+
for (int j = 1; j < temp.length; j++) {
538+
data[j - 1] = temp[j].toDouble();
539+
}
540+
} else {
541+
logger.e("Error: Can't load data");
542+
return false;
543+
}
544+
dChan.loadData(tempMap, data);
545+
546+
dChan.generateAxes();
547+
return true;
548+
}
549+
510550
Future<double> fetchLAChannelFrequency(
511-
int channelNumber, HashMap<String, int> initialStates) async {
551+
int channelNumber, LinkedHashMap<String, int> initialStates) async {
512552
double laChannelFrequency = 0;
513553
DigitalChannel dChan = dChannels[channelNumber];
514554

@@ -538,7 +578,7 @@ class ScienceLab {
538578
if (count == maxSamples / 2 - 1) {
539579
laChannelFrequency = 0;
540580
} else if (yAxis.isNotEmpty &&
541-
yAxis.length != maxSamples / 2 - 2 &&
581+
yAxis.length != maxSamples / 2 - 1 &&
542582
laChannelFrequency != yAxis.length) {
543583
laChannelFrequency = yAxis.length.toDouble();
544584
}
@@ -547,7 +587,7 @@ class ScienceLab {
547587

548588
Future<double> getFrequency(String? channel) async {
549589
channel ??= 'LA1';
550-
HashMap<String, int>? data;
590+
LinkedHashMap<String, int>? data;
551591
try {
552592
await startOneChannelLA(channel, 1, channel, 3);
553593
await Future.delayed(const Duration(milliseconds: 250));
@@ -587,15 +627,111 @@ class ScienceLab {
587627
dChannels[aqChannel].channelName = channel;
588628
if (trMode == 3 || trMode == 4 || trMode == 5) {
589629
dChannels[aqChannel].initialStateOverride = 2;
590-
} else {
630+
} else if (trMode == 2) {
591631
dChannels[aqChannel].initialStateOverride = 1;
592632
}
593633
} catch (e) {
594634
logger.e("Error starting logic analyzer: $e");
595635
}
596636
}
597637

598-
Future<HashMap<String, int>?> getLAInitialStates() async {
638+
Future<void> startTwoChannelLA(
639+
List<String>? channels,
640+
List<int>? modes,
641+
int? maximumTime,
642+
int? trigger,
643+
String? edge,
644+
String? triggerChannel) async {
645+
maximumTime ??= 67;
646+
trigger ??= 0;
647+
edge ??= 'rising';
648+
channels ??= ['LA1', 'LA2'];
649+
modes ??= [1, 1];
650+
List<int> chans = [
651+
calculateDigitalChannel(channels[0])!,
652+
calculateDigitalChannel(channels[1])!
653+
];
654+
triggerChannel ??= channels[0];
655+
if (trigger != 0) {
656+
trigger = 1;
657+
if (edge == 'falling') {
658+
trigger |= 2;
659+
}
660+
trigger |= (calculateDigitalChannel(triggerChannel)! << 4);
661+
}
662+
663+
try {
664+
await clearBuffer(0, maxSamples);
665+
mPacketHandler.sendByte(mCommandsProto.timing);
666+
mPacketHandler.sendByte(mCommandsProto.startTwoChanLa);
667+
mPacketHandler.sendInt((maxSamples / 4).toInt());
668+
mPacketHandler.sendByte(trigger);
669+
mPacketHandler.sendByte(modes[0] | (modes[1] << 4));
670+
mPacketHandler.sendByte(chans[0] | (chans[1] << 4));
671+
await mPacketHandler.getAcknowledgement();
672+
for (int i = 0; i < 2; i++) {
673+
DigitalChannel temp = dChannels[chans[i]];
674+
temp.prescaler = 0;
675+
temp.length = (maxSamples / 4).toInt();
676+
temp.dataType = "long";
677+
temp.maxTime = (maximumTime * 1e6).toInt();
678+
temp.mode = modes[i];
679+
temp.channelNumber = chans[i];
680+
temp.channelName = channels[i];
681+
}
682+
digitalChannelsInBuffer = 2;
683+
} catch (e) {
684+
logger.e("Error starting logic analyzer: $e");
685+
}
686+
}
687+
688+
Future<void> startFourChannelLA(int? trigger, double? maximumTime,
689+
List<int>? modes, String? edge, List<bool>? triggerChannel) async {
690+
trigger ??= 1;
691+
maximumTime ??= 0.001;
692+
modes ??= [1, 1, 1, 1];
693+
edge ??= '0';
694+
await clearBuffer(0, maxSamples);
695+
int prescale = 0;
696+
try {
697+
mPacketHandler.sendByte(mCommandsProto.timing);
698+
mPacketHandler.sendByte(mCommandsProto.startFourChanLa);
699+
mPacketHandler.sendInt((maxSamples / 4).toInt());
700+
mPacketHandler.sendInt(
701+
modes[0] | (modes[1] << 4) | (modes[2] << 8) | (modes[3] << 12));
702+
mPacketHandler.sendByte(prescale);
703+
int triggerOptions = 0;
704+
for (int i = 0; i < 3; i++) {
705+
if (triggerChannel![i]) {
706+
triggerOptions |= (4 << i);
707+
}
708+
}
709+
if (triggerOptions == 0) {
710+
triggerOptions |= 4;
711+
}
712+
if (edge == 'rising') {
713+
triggerOptions |= 2;
714+
}
715+
trigger |= triggerOptions;
716+
mPacketHandler.sendByte(trigger);
717+
await mPacketHandler.getAcknowledgement();
718+
digitalChannelsInBuffer = 4;
719+
int i = 0;
720+
for (DigitalChannel dChan in dChannels) {
721+
dChan.prescaler = prescale;
722+
dChan.dataType = "int";
723+
dChan.length = (maxSamples / 4).toInt();
724+
dChan.maxTime = (maximumTime * 1e6).toInt();
725+
dChan.mode = modes[i];
726+
dChan.channelName = DigitalChannel.digitalChannelNames[i];
727+
i++;
728+
}
729+
} catch (e) {
730+
logger.e("Error starting logic analyzer: $e");
731+
}
732+
}
733+
734+
Future<LinkedHashMap<String, int>?> getLAInitialStates() async {
599735
try {
600736
mPacketHandler.sendByte(mCommandsProto.timing);
601737
mPacketHandler.sendByte(mCommandsProto.getInitialDigitalStates);
@@ -654,7 +790,7 @@ class ScienceLab {
654790
D = 0;
655791
}
656792

657-
HashMap<String, int> retData = HashMap<String, int>();
793+
LinkedHashMap<String, int> retData = LinkedHashMap<String, int>();
658794
retData['A'] = A;
659795
retData['B'] = B;
660796
retData['C'] = C;

lib/constants.dart

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,33 @@ String wifi = 'WIFI';
121121
String whatisPslab = 'What is PSLab Device?';
122122
String pslabUrl = 'https://pslab.io';
123123

124+
String logicAnalyzer = 'Logic Analyzer';
125+
String channelSelection = 'Channel Selection';
126+
String logicAnalyzerAxisTitle = 'Time (\u00B5s)';
127+
String noChartDataAvailable = 'No chart data available';
128+
String noOfChannelsOne = '1';
129+
String noOfChannelsTwo = '2';
130+
String noOfChannelsThree = '3';
131+
String noOfChannelsFour = '4';
132+
String channelLA1 = 'LA1';
133+
String channelLA2 = 'LA2';
134+
String channelLA3 = 'LA3';
135+
String channelLA4 = 'LA4';
136+
List<String> channelNames = [
137+
channelLA1,
138+
channelLA2,
139+
channelLA3,
140+
channelLA4,
141+
];
142+
List<String> analysisOptions = [
143+
'EVERY EDGE',
144+
'EVERY FALLING EDGE',
145+
'EVERY RISING EDGE',
146+
'EVERY FOURTH RISING EDGE',
147+
'DISABLED'
148+
];
149+
String analyze = 'ANALYZE';
150+
124151
String settings = 'Settings';
125152
String start = 'Auto Start';
126153
String autoStartText = 'Auto start app when PSLab device is connected';

lib/main.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import 'package:pslab/view/connect_device_screen.dart';
88
import 'package:pslab/view/faq_screen.dart';
99
import 'package:pslab/view/gyroscope_screen.dart';
1010
import 'package:pslab/view/instruments_screen.dart';
11+
import 'package:pslab/view/logic_analyzer_screen.dart';
1112
import 'package:pslab/view/luxmeter_screen.dart';
1213
import 'package:pslab/view/multimeter_screen.dart';
1314
import 'package:pslab/view/oscilloscope_screen.dart';
@@ -51,6 +52,7 @@ class MyApp extends StatelessWidget {
5152
'/': (context) => const InstrumentsScreen(),
5253
'/oscilloscope': (context) => const OscilloscopeScreen(),
5354
'/multimeter': (context) => const MultimeterScreen(),
55+
'/logicAnalyzer': (context) => const LogicAnalyzerScreen(),
5456
'/connectDevice': (context) => const ConnectDeviceScreen(),
5557
'/faq': (context) => const FAQScreen(),
5658
'/settings': (context) => const SettingsScreen(),

0 commit comments

Comments
 (0)