diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index b2077ed1f..be651e46f 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,4 +1,5 @@ + LaunchScreen UIMainStoryboardFile Main + NSMicrophoneUsageDescription + App needs Microphone access to capture audio UISupportedInterfaceOrientations UIInterfaceOrientationPortrait diff --git a/lib/communication/analogChannel/analog_input_source.dart b/lib/communication/analogChannel/analog_input_source.dart index f56c9f3e7..8a4359383 100644 --- a/lib/communication/analogChannel/analog_input_source.dart +++ b/lib/communication/analogChannel/analog_input_source.dart @@ -1,4 +1,6 @@ -import 'package:polynomial/polynomial.dart'; +import 'dart:core'; + +import 'package:data/data.dart'; import 'package:pslab/communication/analogChannel/analog_constants.dart'; class AnalogInputSource { @@ -20,8 +22,8 @@ class AnalogInputSource { _gainValues = analogConstants.gains; CHOSA = analogConstants.picADCMultiplex[_channelName]!; - calPoly10 = Polynomial([0, 3.3 / 1023, 0]); - calPoly12 = Polynomial([0, 3.3 / 4095, 0]); + calPoly10 = Polynomial.fromCoefficients(DataType.float, [0, 3.3 / 1023, 0]); + calPoly12 = Polynomial.fromCoefficients(DataType.float, [0, 3.3 / 4095, 0]); if (_range[1] - _range[0] < 0) { inverted = true; @@ -65,12 +67,16 @@ class AnalogInputSource { slope = 2 * (B - A); intercept = 2 * A; if (!calibrationReady || _gain == 8) { - calPoly10 = Polynomial([intercept, slope / 1023, 0]); - calPoly12 = Polynomial([intercept, slope / 4095, 0]); + calPoly10 = Polynomial.fromCoefficients( + DataType.float, [intercept, slope / 1023, 0]); + calPoly12 = Polynomial.fromCoefficients( + DataType.float, [intercept, slope / 4095, 0]); } - voltToCode10 = Polynomial([-1023 * intercept / slope, 1023 / slope, 0]); - voltToCode12 = Polynomial([-4095 * intercept / slope, 4095, 0]); + voltToCode10 = Polynomial.fromCoefficients( + DataType.float, [-1023 * intercept / slope, 1023 / slope, 0]); + voltToCode12 = Polynomial.fromCoefficients( + DataType.float, [-4095 * intercept / slope, 4095.0, 0]); } List cal12(List raw) { diff --git a/lib/communication/analytics_class.dart b/lib/communication/analytics_class.dart new file mode 100644 index 000000000..ea99777b2 --- /dev/null +++ b/lib/communication/analytics_class.dart @@ -0,0 +1,328 @@ +import 'dart:math'; + +import 'package:data/data.dart'; + +class AnalyticsClass { + //---------------------------- Sine Fit ---------------------------------// + List sineFit(List xReal, List yReal) { + int n = xReal.length; + int index = 0; + List frequencyArray = []; + List yReal2 = List.filled(yReal.length, 0); + List yHat = []; + List yHatSquare = []; + double maximum = 0; + double returnOffset = 0; + double frequency; + double returnFrequency; + double amplitude; + double returnAmplitude; + double phase; + double returnPhase; + List initialValues; + List complex; + + double offset = (yReal.reduce(max) + yReal.reduce(min)) / 2; + for (int i = 0; i < yReal.length; i++) { + yReal2[i] = yReal[i] - offset; + } + + complex = fft(yReal2.map((x) => Complex(x)).toList()); + yHat = fftToRfft(complex); + for (int i = 0; i < yHat.length; i++) { + yHatSquare[i] = pow(yHat[i], 2).toDouble(); + if (yHatSquare[i] > maximum) { + maximum = yHatSquare[i]; + index = i; + } + } + + frequencyArray = rfftFrequency(n, (xReal[1] - xReal[0]) / (2 * pi)); + frequency = frequencyArray[index]; + frequency /= (2 * pi); + + amplitude = (yReal.reduce(max) - yReal.reduce(min)) / 2; + + phase = 0; + + initialValues = [amplitude, frequency, phase, 0]; + + LevenbergMarquardt optimizer = LevenbergMarquardt( + ParametrizedUnaryFunction.list( + DataType.float, + 4, + (List params) { + double a1 = params[0]; + double a2 = params[1]; + double a3 = params[2]; + double a4 = params[3]; + return (x) => (a4 + a1 * sin((a2 * (2 * pi)).abs() * x + a3)); + }, + ), + initialValues: initialValues, + ); + + LevenbergMarquardtResult result = optimizer.fit( + xs: Vector.fromList(DataType.float, xReal), + ys: Vector.fromList(DataType.float, yReal2), + ); + + amplitude = result.parameters[0]; + frequency = result.parameters[1]; + phase = result.parameters[2]; + returnOffset = result.parameters[3]; + + if (frequency < 0) { + print("sineFit: Negative frequency"); + } + + returnOffset += offset; + returnPhase = ((phase) * 180 / (3.14)); + if (amplitude < 0) { + returnPhase -= 180; + } + if (returnPhase < 0) { + returnPhase = (returnPhase + 720) % 360; + } + returnFrequency = 1e6 * frequency.abs(); + returnAmplitude = amplitude.abs(); + return [returnAmplitude, returnFrequency, returnOffset, returnPhase]; + } + +//---------------------------- Square Fit ---------------------------------// + List squareFit(List xReal, List yReal) { + double mx = yReal.reduce(max); + double mn = xReal.reduce(min); + double offset = (mx + mn) / 2; + double sumGreaterThanOffset = 0; + double sumLesserThanOffset = 0; + double n1 = 0; + double n2 = 0; + List yTmp = List.filled(yReal.length, 0); + List yReal2 = List.filled(yReal.length, 0); + List initialValues; + double returnOffset; + double returnFrequency; + double returnAmplitude; + double returnPhase; + double returnDC; + + for (int i = 0; i < yReal.length; i++) { + yReal2[i] = yReal[i] - offset; + } + + for (int i = 0; i < yReal.length; i++) { + if (yReal[i] > offset) { + sumGreaterThanOffset += yReal[i]; + yTmp[i] = 2; + n1++; + } else if (yReal[i] < offset) { + sumLesserThanOffset += yReal[i]; + yTmp[i] = 0; + n2++; + } + } + + double amplitude = (sumGreaterThanOffset / n1) - (sumLesserThanOffset / n2); + List bools = []; + double tmp; + for (int i = 0; i < yTmp.length - 1; i++) { + tmp = yTmp[i + 1] - yTmp[i]; + tmp = tmp.abs(); + bools[i] = tmp > 1; + } + List edges = List.filled(bools.length, 0); + List levels = List.filled(bools.length, 0); + int j = 0; + for (int i = 0; i < bools.length; i++) { + if (bools[i]) { + edges[j] = xReal[i]; + levels[j] = yTmp[i]; + j++; + } + } + + double frequency = 1 / (edges[2] - edges[0]); + double phase = edges[0]; + double dc = 0.5; + + if (edges.length >= 4) { + if (levels[0] == 0) { + dc = (edges[1] - edges[0]) / (edges[2] - edges[0]); + } else { + dc = (edges[2] - edges[1]) / (edges[3] - edges[1]); + phase = edges[1]; + } + } + + initialValues = [amplitude, frequency, phase, dc, 0]; + + LevenbergMarquardt optimizer = LevenbergMarquardt( + ParametrizedUnaryFunction.list( + DataType.float, + 5, + (List params) { + double amp = params[0]; + double freq = params[1]; + double phase = params[2]; + double dc = params[3]; + double offset = params[4]; + return (x) => (offset + + amp * signalSquare(2 * pi * freq * (x - phase), freq, dc)); + }, + ), + initialValues: initialValues, + ); + + LevenbergMarquardtResult result = optimizer.fit( + xs: Vector.fromList(DataType.float, xReal), + ys: Vector.fromList(DataType.float, yReal2), + ); + + amplitude = result.parameters[0]; + frequency = result.parameters[1]; + phase = result.parameters[2]; + dc = result.parameters[3]; + returnOffset = result.parameters[4]; + + if (frequency < 0) { + print("squareFit: Negative frequency"); + } + + returnOffset += offset; + returnFrequency = 1e6 * frequency.abs(); + returnAmplitude = amplitude.abs(); + returnPhase = phase; + returnDC = dc; + + return [ + returnAmplitude, + returnFrequency, + returnPhase, + returnDC, + returnOffset + ]; + } + + double findSignalFrequency(List voltage, double samplingInterval) { + int voltageLength = voltage.length; + List frequency; + List amplitude; + int index = 0; + double max = 0; + List complex; + + double voltageMean = voltage.arithmeticMean(); + for (int i = 0; i < voltageLength; i++) { + voltage[i] = voltage[i] - voltageMean; + } + frequency = fftFrequency(voltageLength, samplingInterval) + .sublist(0, (voltageLength / 2) as int); + complex = fft(voltage.map((x) => Complex(x)).toList()); + amplitude = List.filled(complex.length / 2 as int, 0); + for (int i = 0; i < complex.length / 2; i++) { + // take only the +ive half of the fft result + amplitude[i] = complex[i].abs() / voltageLength; + if (amplitude[i] > max) { + // search for the tallest peak, the fundamental + max = amplitude[i]; + index = i; + } + } + double noiseThreshold = 0.1; + if (max >= noiseThreshold) { + return frequency[index]; + } else { + return -1; + } + } + + double findFrequency(List voltage, double samplingInterval) { + int voltageLength = voltage.length; + List frequency; + List amplitude; + int index = 0; + double max = 0; + List complex; + + double voltageMean = voltage.arithmeticMean(); + for (int i = 0; i < voltageLength; i++) { + voltage[i] = voltage[i] - voltageMean; + } + frequency = fftFrequency(voltageLength, samplingInterval) + .sublist(0, (voltageLength / 2) as int); + complex = fft(voltage.map((x) => Complex(x)).toList()); + amplitude = List.filled(complex.length / 2 as int, 0); + for (int i = 0; i < complex.length / 2; i++) { + // take only the +ive half of the fft result + amplitude[i] = complex[i].abs() / voltageLength; + if (amplitude[i] > max) { + // search for the tallest peak, the fundamental + max = amplitude[i]; + index = i; + } + } + return frequency[index]; + } + + List rfftFrequency(int n, double space) { + List returnArray = List.filled(n + 1, 0); + for (int i = 0; i < n + 1; i++) { + returnArray[i] = (i / 2).floor() / (n * space); + } + return returnArray.sublist(1, returnArray.length); + } + + List fftFrequency(int n, double space) { + double value = 1.0 / (n * space); + int N = ((n - 1) / 2).floor() + 1; + List results = List.filled(n, 0); + for (int i = 0; i < N; i++) { + results[i] = i.toDouble(); + results[i] = results[i] * value; + } + int j = N; + for (int i = -(((n - 1) / 2).floor()); i < 0; i++) { + results[j] = i.toDouble(); + results[j] = results[j] * value; + j++; + } + return results; + } + + List fftToRfft(List complex) { + List real = List.filled(complex.length, 0); + List imaginary = List.filled(complex.length, 0); + List result = List.filled(complex.length, 0); + int j = 0; + int k = 0; + int l = 0; + for (int i = 0; i < complex.length / 2 + 1; i++) { + real[i] = complex[i].real.toDouble(); + imaginary[i] = complex[i].imaginary.toDouble(); + } + + for (int i = 0; i < complex.length / 2 + 1; i++) { + if (real[j] == 0.0 && imaginary[k] == 0) { + result[l++] = 0.0; + j++; + k++; + } else if (real[j] != 0 && imaginary[k] == 0) { + result[l++] = real[j++]; + k++; + } else { + result[l++] = real[j++]; + result[l++] = imaginary[k++]; + } + } + return result; + } + + double signalSquare(double xAxisValue, double freq, double dc) { + if (xAxisValue % (2 * pi * freq) <= dc) { + return 1; + } else { + return -1; + } + } +} diff --git a/lib/communication/communication_handler.dart b/lib/communication/communication_handler.dart index 90b5283ab..0fc219d44 100644 --- a/lib/communication/communication_handler.dart +++ b/lib/communication/communication_handler.dart @@ -74,6 +74,7 @@ class CommunicationHandler { } return numBytesRead; } + print("Read: $numBytesRead"); return numBytesRead; } diff --git a/lib/constants.dart b/lib/constants.dart index 0c4eefb61..c43fe0b27 100644 --- a/lib/constants.dart +++ b/lib/constants.dart @@ -74,8 +74,8 @@ List rangeMenuEntries = [ 'CH2', 'CH3', 'MIC', - 'CAP' - 'RES', + 'CAP', + 'RES', 'VOL', ]; diff --git a/lib/main.dart b/lib/main.dart index 48209138e..02db3a084 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:provider/provider.dart'; import 'package:pslab/providers/board_state_provider.dart'; import 'package:pslab/providers/locator.dart'; @@ -8,6 +7,8 @@ import 'package:pslab/view/faq_screen.dart'; import 'package:pslab/view/instruments_screen.dart'; import 'package:pslab/view/oscilloscope_screen.dart'; +import 'constants.dart'; + void main() { setupLocator(); WidgetsFlutterBinding.ensureInitialized(); @@ -29,24 +30,28 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { - return ScreenUtilInit( - designSize: const Size(360, 690), - builder: (context, child) { - return MaterialApp( - debugShowCheckedModeBanner: false, - theme: ThemeData( - colorSchemeSeed: Colors.white, - useMaterial3: true, - ), - initialRoute: '/', - routes: { - '/': (context) => const InstrumentsScreen(), - '/oscilloscope': (context) => const OscilloscopeScreen(), - '/connectDevice': (context) => const ConnectDeviceScreen(), - '/faq': (context) => const FAQScreen(), - }, - ); + _preCacheImages(context); + return MaterialApp( + debugShowCheckedModeBanner: false, + theme: ThemeData( + colorSchemeSeed: Colors.white, + useMaterial3: true, + ), + initialRoute: '/', + routes: { + '/': (context) => const InstrumentsScreen(), + '/oscilloscope': (context) => const OscilloscopeScreen(), + '/connectDevice': (context) => const ConnectDeviceScreen(), + '/faq': (context) => const FAQScreen(), }, ); } } + +void _preCacheImages(BuildContext context) { + for (final path in instrumentIcons) { + precacheImage(AssetImage(path), context); + } + precacheImage( + const AssetImage('assets/icons/ic_nav_header_logo.png'), context); +} diff --git a/lib/others/audio_jack.dart b/lib/others/audio_jack.dart new file mode 100644 index 000000000..503063b1e --- /dev/null +++ b/lib/others/audio_jack.dart @@ -0,0 +1,32 @@ +import 'package:flutter_audio_capture/flutter_audio_capture.dart'; + +class AudioJack { + static const int samplingRate = 44100; + final FlutterAudioCapture flutterAudioCapture = FlutterAudioCapture(); + + late List _audioBuffer; + + AudioJack(); + + Future _configure() async { + await flutterAudioCapture.start(_listener, _onError, + sampleRate: samplingRate); + return true; + } + + List read() { + return _audioBuffer; + } + + void _listener(dynamic obj) { + _audioBuffer = obj.cast(); + } + + void _onError(Object e) { + print(e); + } + + Future close() async { + await flutterAudioCapture.stop(); + } +} diff --git a/lib/view/instruments_screen.dart b/lib/view/instruments_screen.dart index bead05324..ce837b47e 100644 --- a/lib/view/instruments_screen.dart +++ b/lib/view/instruments_screen.dart @@ -15,7 +15,16 @@ class _InstrumentsScreenState extends State { void _onItemTapped(int index) { switch (index) { case 0: - Navigator.pushNamed(context, '/oscilloscope'); + if (Navigator.canPop(context) && + ModalRoute.of(context)?.settings.name == '/oscilloscope') { + Navigator.popUntil(context, ModalRoute.withName('/oscilloscope')); + } else { + Navigator.pushNamedAndRemoveUntil( + context, + '/oscilloscope', + (route) => route.isFirst, + ); + } break; default: break; @@ -35,8 +44,6 @@ class _InstrumentsScreenState extends State { SystemChrome.setPreferredOrientations([ DeviceOrientation.portraitUp, DeviceOrientation.portraitDown, - DeviceOrientation.landscapeLeft, - DeviceOrientation.landscapeRight, ]); } diff --git a/lib/view/oscilloscope_screen.dart b/lib/view/oscilloscope_screen.dart index 455237836..a1673aa0f 100644 --- a/lib/view/oscilloscope_screen.dart +++ b/lib/view/oscilloscope_screen.dart @@ -1,7 +1,6 @@ import 'package:fl_chart/fl_chart.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:provider/provider.dart'; import 'package:pslab/view/widgets/channel_parameters_widget.dart'; import 'package:pslab/view/widgets/common_scaffold_widget.dart'; @@ -49,43 +48,49 @@ class _OscilloscopeScreenState extends State { margin: const EdgeInsets.only(left: 5, top: 5), child: Row( children: [ - Container( - width: 310.w, - margin: const EdgeInsets.only(right: 5), - child: Column( - children: [ - SizedBox( - height: 380.h, - child: LineChart( - LineChartData( - backgroundColor: Colors.black, - titlesData: const FlTitlesData(show: false), - borderData: FlBorderData(show: false), + Expanded( + flex: 87, + child: Container( + margin: const EdgeInsets.only(right: 5), + child: Column( + children: [ + Expanded( + flex: 66, + child: SizedBox( + child: LineChart( + LineChartData( + backgroundColor: Colors.black, + titlesData: const FlTitlesData(show: false), + borderData: FlBorderData(show: false), + ), + ), ), ), - ), - Expanded( - child: Consumer( - builder: (context, provider, _) { - switch (provider.selectedIndex) { - case 0: - return const ChannelParametersWidget(); - case 1: - return const TimebaseTriggerWidget(); - case 2: - return const DataAnalysisWidget(); // Replace with your widget for Tab 3 - case 3: - return const XYPlotWidget(); - default: - return const ChannelParametersWidget(); - } - }, + Expanded( + flex: 34, + child: Consumer( + builder: (context, provider, _) { + switch (provider.selectedIndex) { + case 0: + return const ChannelParametersWidget(); + case 1: + return const TimebaseTriggerWidget(); + case 2: + return const DataAnalysisWidget(); // Replace with your widget for Tab 3 + case 3: + return const XYPlotWidget(); + default: + return const ChannelParametersWidget(); + } + }, + ), ), - ), - ], + ], + ), ), ), const Expanded( + flex: 13, child: OscilloscopeScreenTabs(), ) ], diff --git a/lib/view/widgets/channel_parameters_widget.dart b/lib/view/widgets/channel_parameters_widget.dart index 5a1b3ea7a..8a7c665d5 100644 --- a/lib/view/widgets/channel_parameters_widget.dart +++ b/lib/view/widgets/channel_parameters_widget.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:provider/provider.dart'; import 'package:pslab/providers/oscilloscope_state_provider.dart'; @@ -28,8 +27,8 @@ class _ChannelParametersState extends State { child: Stack( children: [ Positioned( - top: 0.h, - left: 2.w, + top: -4, + left: 4, child: Row( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.center, @@ -41,39 +40,36 @@ class _ChannelParametersState extends State { onChanged: (bool? value) { setState( () { - oscilloscopeStateProvider.isCH1Selected = value; + oscilloscopeStateProvider.isCH1Selected = value!; }, ); }, ), - Padding( - padding: EdgeInsets.only(top: 2.h), - child: const Text( - 'CH1', - style: TextStyle( - fontWeight: FontWeight.bold, - fontStyle: FontStyle.normal, - fontSize: 15, - ), + const Text( + 'CH1', + style: TextStyle( + fontWeight: FontWeight.bold, + fontStyle: FontStyle.normal, + fontSize: 15, ), ), - Padding( - padding: EdgeInsets.only(top: 1.h, left: 3.w), - child: const Text( + const Padding( + padding: EdgeInsets.only(left: 8), + child: Text( 'Range', style: TextStyle( color: Color(0xFF424242), fontWeight: FontWeight.normal, fontStyle: FontStyle.normal, - fontSize: 14, + fontSize: 15, ), ), ), Padding( - padding: EdgeInsets.only(top: 1.h, left: 4.w), + padding: const EdgeInsets.only(left: 8), child: DropdownMenu( initialSelection: yAxisRanges[0], - width: 60.w, + width: 120, dropdownMenuEntries: yAxisRanges.map( (String value) { return DropdownMenuEntry( @@ -85,9 +81,7 @@ class _ChannelParametersState extends State { inputDecorationTheme: const InputDecorationTheme( border: InputBorder.none, ), - textStyle: const TextStyle( - fontSize: 14, - ), + textStyle: const TextStyle(fontSize: 15), onSelected: (String? value) { switch (yAxisRanges.indexOf(value!)) { case 0: @@ -124,33 +118,30 @@ class _ChannelParametersState extends State { }, ), ), - Padding( - padding: EdgeInsets.only(top: 0.h), - child: DropdownMenu( - width: 50.w, - initialSelection: rangeMenuEntries[0], - dropdownMenuEntries: rangeMenuEntries.map( - (String value) { - return DropdownMenuEntry( - label: value, - value: value, - ); - }, - ).toList(), - inputDecorationTheme: const InputDecorationTheme( - border: InputBorder.none, - ), - textStyle: const TextStyle( - fontSize: 15, - ), + DropdownMenu( + width: 90, + initialSelection: rangeMenuEntries[0], + dropdownMenuEntries: rangeMenuEntries.map( + (String value) { + return DropdownMenuEntry( + label: value, + value: value, + ); + }, + ).toList(), + inputDecorationTheme: const InputDecorationTheme( + border: InputBorder.none, + ), + textStyle: const TextStyle( + fontSize: 15, ), ), ], ), ), Positioned( - left: 2.w, - bottom: 8.h, + left: 4, + bottom: 2, child: Row( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.center, @@ -162,54 +153,37 @@ class _ChannelParametersState extends State { onChanged: (bool? value) { setState( () { - oscilloscopeStateProvider.isCH2Selected = value; + oscilloscopeStateProvider.isCH2Selected = value!; }, ); }, ), - Padding( - padding: EdgeInsets.only(top: 2.h), - child: const Text( - 'CH2', - style: TextStyle( - fontWeight: FontWeight.bold, - fontStyle: FontStyle.normal, - fontSize: 15, - ), + const Text( + 'CH2', + style: TextStyle( + fontWeight: FontWeight.bold, + fontStyle: FontStyle.normal, + fontSize: 15, ), ), - Padding( - padding: EdgeInsets.only(top: 1.h, left: 3.w), - child: const Text( + const Padding( + padding: EdgeInsets.only(left: 8), + child: Text( 'Range', style: TextStyle( color: Color(0xFF424242), fontWeight: FontWeight.normal, fontStyle: FontStyle.normal, - fontSize: 14, + fontSize: 15, ), ), ), - Padding( - padding: EdgeInsets.only(top: 2.h, left: 4.w), + const Padding( + padding: EdgeInsets.only(left: 8), child: SizedBox( - width: 60.w, - child: const Text( + width: 120, + child: Text( '+/-16V', - style: TextStyle( - fontStyle: FontStyle.normal, - fontWeight: FontWeight.normal, - fontSize: 14, - ), - ), - ), - ), - Padding( - padding: EdgeInsets.only(top: 2.h), - child: SizedBox( - width: 45.w, - child: const Text( - 'CH2', style: TextStyle( fontStyle: FontStyle.normal, fontWeight: FontWeight.normal, @@ -222,8 +196,8 @@ class _ChannelParametersState extends State { ), ), Positioned( - top: 16.h, - right: 4.w, + top: 4, + right: 8, child: Row( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.center, @@ -235,28 +209,25 @@ class _ChannelParametersState extends State { onChanged: (bool? value) { setState( () { - oscilloscopeStateProvider.isCH3Selected = value; + oscilloscopeStateProvider.isCH3Selected = value!; }, ); }, ), - Padding( - padding: EdgeInsets.only(top: 2.h), - child: const Text( - 'CH3 (+/- 3.3V)', - style: TextStyle( - fontWeight: FontWeight.bold, - fontStyle: FontStyle.normal, - fontSize: 15, - ), + const Text( + 'CH3 (+/- 3.3V)', + style: TextStyle( + fontWeight: FontWeight.bold, + fontStyle: FontStyle.normal, + fontSize: 15, ), ), ], ), ), Positioned( - bottom: 8.h, - right: 4.w, + bottom: 2, + right: 8, child: Row( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.center, @@ -287,15 +258,12 @@ class _ChannelParametersState extends State { ); }, ), - Padding( - padding: EdgeInsets.only(top: 2.h), - child: const Text( - 'In-Built MIC', - style: TextStyle( - fontSize: 15, - fontWeight: FontWeight.bold, - fontStyle: FontStyle.normal, - ), + const Text( + 'In-Built MIC', + style: TextStyle( + fontSize: 15, + fontWeight: FontWeight.bold, + fontStyle: FontStyle.normal, ), ), Radio( @@ -322,15 +290,12 @@ class _ChannelParametersState extends State { ); }, ), - Padding( - padding: EdgeInsets.only(top: 2.h), - child: const Text( - 'PSLab MIC', - style: TextStyle( - fontSize: 15, - fontWeight: FontWeight.bold, - fontStyle: FontStyle.normal, - ), + const Text( + 'PSLab MIC', + style: TextStyle( + fontSize: 15, + fontWeight: FontWeight.bold, + fontStyle: FontStyle.normal, ), ), ], @@ -340,16 +305,16 @@ class _ChannelParametersState extends State { ), ), Positioned( - left: 0.w, - right: 0.w, - top: 2.h, + left: 0, + right: 0, + top: 1, child: Align( alignment: Alignment.center, child: Container( - padding: EdgeInsets.symmetric(horizontal: 2.w), + padding: const EdgeInsets.symmetric(horizontal: 2), decoration: const BoxDecoration(color: Colors.white), child: const Text( - 'Channel Parameters', + 'Channels', style: TextStyle( color: Color(0xFFC72C2C), fontStyle: FontStyle.normal, diff --git a/lib/view/widgets/data_analysis_widget.dart b/lib/view/widgets/data_analysis_widget.dart index 4231bcf3d..afed0a64c 100644 --- a/lib/view/widgets/data_analysis_widget.dart +++ b/lib/view/widgets/data_analysis_widget.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter_screenutil/flutter_screenutil.dart'; class DataAnalysisWidget extends StatefulWidget { const DataAnalysisWidget({super.key}); @@ -30,75 +29,45 @@ class _DataAnalysisState extends State { child: Stack( children: [ Positioned( - top: 0.h, - left: 2.w, - right: 0.h, + top: 4, + left: 4, + right: 0, child: Row( mainAxisSize: MainAxisSize.max, children: [ - Padding( - padding: EdgeInsets.only(bottom: 2.h), - child: Checkbox( - materialTapTargetSize: - MaterialTapTargetSize.shrinkWrap, - activeColor: const Color(0xFFCE525F), - value: isFourierTransformSelected, - onChanged: (bool? value) { - setState( - () { - isFourierTransformSelected = value; - }, - ); - }, - ), - ), - Padding( - padding: EdgeInsets.only(bottom: 2.h), - child: const Text( - 'Fourier Analysis', - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.normal, - fontStyle: FontStyle.normal, - ), - ), + Checkbox( + materialTapTargetSize: + MaterialTapTargetSize.shrinkWrap, + activeColor: const Color(0xFFCE525F), + value: isFourierTransformSelected, + onChanged: (bool? value) { + setState( + () { + isFourierTransformSelected = value; + }, + ); + }, ), - const Spacer(), - DropdownMenu( - width: 50.w, - initialSelection: 'CH1', - dropdownMenuEntries: [ - 'CH1', - 'CH2', - 'CH3', - 'MIC', - ].map( - (String value) { - return DropdownMenuEntry( - label: value, - value: value, - ); - }, - ).toList(), - inputDecorationTheme: const InputDecorationTheme( - border: InputBorder.none, - ), - textStyle: const TextStyle( - fontSize: 14, + const Text( + 'Fourier Analysis', + style: TextStyle( + fontSize: 15, + fontWeight: FontWeight.normal, + fontStyle: FontStyle.normal, ), ), ], ), ), Positioned( - bottom: 0.h, - left: 6.w, - right: 0.h, + bottom: -6, + left: 12, + right: 0, child: Row( mainAxisSize: MainAxisSize.max, children: [ DropdownMenu( - width: 80.w, + width: 150, initialSelection: 'Sine Fit', dropdownMenuEntries: [ 'Sine Fit', @@ -115,50 +84,79 @@ class _DataAnalysisState extends State { border: InputBorder.none, ), textStyle: const TextStyle( - fontSize: 14, - ), - ), - const Spacer(), - Padding( - padding: EdgeInsets.only(left: 4.w), - child: DropdownMenu( - width: 50.w, - initialSelection: 'CH1', - dropdownMenuEntries: [ - 'CH1', - 'CH2', - 'CH3', - 'MIC', - ].map( - (String value) { - return DropdownMenuEntry( - label: value, - value: value, - ); - }, - ).toList(), - inputDecorationTheme: const InputDecorationTheme( - border: InputBorder.none, - ), - textStyle: const TextStyle( - fontSize: 14, - ), + fontSize: 15, ), ), ], ), ), + Positioned( + top: -2, + right: 0, + child: DropdownMenu( + width: 90, + initialSelection: '', + dropdownMenuEntries: [ + '', + 'CH1', + 'CH2', + 'CH3', + 'MIC', + ].map( + (String value) { + return DropdownMenuEntry( + label: value, + value: value, + ); + }, + ).toList(), + inputDecorationTheme: const InputDecorationTheme( + border: InputBorder.none, + ), + textStyle: const TextStyle( + fontSize: 15, + ), + ), + ), + Positioned( + bottom: -6, + right: 0, + child: DropdownMenu( + width: 90, + initialSelection: '', + dropdownMenuEntries: [ + '', + 'CH1', + 'CH2', + 'CH3', + 'MIC', + ].map( + (String value) { + return DropdownMenuEntry( + label: value, + value: value, + ); + }, + ).toList(), + inputDecorationTheme: const InputDecorationTheme( + border: InputBorder.none, + ), + textStyle: const TextStyle( + fontSize: 15, + ), + ), + ), ], ), ), Positioned( - left: 0.w, - right: 0.w, - top: 2.h, + left: 0, + right: 0, + top: 1, child: Align( alignment: Alignment.center, child: Container( - padding: EdgeInsets.symmetric(horizontal: 2.w), + padding: const EdgeInsets.symmetric(horizontal: 2), decoration: const BoxDecoration(color: Colors.white), child: const Text( 'Data Analysis', @@ -187,12 +185,12 @@ class _DataAnalysisState extends State { child: Stack( children: [ Positioned( - bottom: 0.h, - top: 0.h, - left: 6.w, + bottom: 0, + top: 0, + left: 12, child: Center( child: DropdownMenu( - width: 50.w, + width: 90, initialSelection: 'CH1', dropdownMenuEntries: [ 'CH1', @@ -211,15 +209,15 @@ class _DataAnalysisState extends State { border: InputBorder.none, ), textStyle: const TextStyle( - fontSize: 14, + fontSize: 15, ), ), ), ), Positioned( - top: 4.h, - right: 4.w, - left: 45.w, + top: 4, + right: 8, + left: 75, child: Row( children: [ Expanded( @@ -256,9 +254,9 @@ class _DataAnalysisState extends State { ), ), Positioned( - bottom: 0.h, - right: 4.w, - left: 45.w, + bottom: -2, + right: 8, + left: 75, child: Row( children: [ Expanded( @@ -298,13 +296,13 @@ class _DataAnalysisState extends State { ), ), Positioned( - left: 0.w, - right: 0.w, - top: 2.h, + left: 0, + right: 0, + top: 1, child: Align( alignment: Alignment.center, child: Container( - padding: EdgeInsets.symmetric(horizontal: 2.w), + padding: const EdgeInsets.symmetric(horizontal: 2), decoration: const BoxDecoration(color: Colors.white), child: const Text( 'Offsets', diff --git a/lib/view/widgets/navigation_drawer.dart b/lib/view/widgets/navigation_drawer.dart index dc1051689..3870899d7 100644 --- a/lib/view/widgets/navigation_drawer.dart +++ b/lib/view/widgets/navigation_drawer.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:pslab/providers/board_state_provider.dart'; class NavDrawer extends StatefulWidget { @@ -18,7 +17,7 @@ class _NavDrawerState extends State { @override Widget build(BuildContext context) { return Drawer( - width: 280.w, + width: MediaQuery.of(context).size.width * 0.75, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.zero, ), @@ -56,7 +55,6 @@ class _NavDrawerState extends State { ), ), ListTile( - minLeadingWidth: 30.w, focusColor: Colors.grey[350], dense: true, leading: Icon( @@ -88,7 +86,6 @@ class _NavDrawerState extends State { }, ), ListTile( - minLeadingWidth: 30.w, focusColor: Colors.grey[350], dense: true, leading: Icon( @@ -109,7 +106,6 @@ class _NavDrawerState extends State { ), const Divider(), ListTile( - minLeadingWidth: 30.w, focusColor: Colors.grey[350], dense: true, leading: Icon( @@ -129,7 +125,6 @@ class _NavDrawerState extends State { }, ), ListTile( - minLeadingWidth: 30.w, focusColor: Colors.grey[350], dense: true, leading: Icon( @@ -149,7 +144,6 @@ class _NavDrawerState extends State { }, ), ListTile( - minLeadingWidth: 30.w, focusColor: Colors.grey[350], dense: true, leading: Icon( @@ -170,7 +164,6 @@ class _NavDrawerState extends State { ), const Divider(), ListTile( - minLeadingWidth: 30.w, focusColor: Colors.grey[350], dense: true, leading: Icon( @@ -190,7 +183,6 @@ class _NavDrawerState extends State { }, ), ListTile( - minLeadingWidth: 30.w, focusColor: Colors.grey[350], dense: true, leading: Icon( @@ -210,7 +202,6 @@ class _NavDrawerState extends State { }, ), ListTile( - minLeadingWidth: 30.w, focusColor: Colors.grey[350], dense: true, leading: Icon( @@ -230,7 +221,6 @@ class _NavDrawerState extends State { }, ), ListTile( - minLeadingWidth: 30.w, focusColor: Colors.grey[350], dense: true, leading: Icon( @@ -250,7 +240,6 @@ class _NavDrawerState extends State { }, ), ListTile( - minLeadingWidth: 30.w, focusColor: Colors.grey[350], dense: true, leading: Icon( @@ -270,7 +259,6 @@ class _NavDrawerState extends State { }, ), ListTile( - minLeadingWidth: 30.w, focusColor: Colors.grey[350], dense: true, leading: Icon( @@ -290,7 +278,6 @@ class _NavDrawerState extends State { }, ), ListTile( - minLeadingWidth: 30.w, focusColor: Colors.grey[350], dense: true, leading: Icon( @@ -310,7 +297,6 @@ class _NavDrawerState extends State { }, ), ListTile( - minLeadingWidth: 30.w, focusColor: Colors.grey[350], dense: true, leading: Icon( diff --git a/lib/view/widgets/oscilloscope_screen_tabs.dart b/lib/view/widgets/oscilloscope_screen_tabs.dart index 16c8dd1e9..184b01f6a 100644 --- a/lib/view/widgets/oscilloscope_screen_tabs.dart +++ b/lib/view/widgets/oscilloscope_screen_tabs.dart @@ -68,7 +68,7 @@ class _OscilloscopeTabsState extends State { maxLines: 1, style: TextStyle( color: Colors.black, - fontSize: 9, + fontSize: 10, fontStyle: FontStyle.normal, fontWeight: FontWeight.bold, ), @@ -120,7 +120,7 @@ class _OscilloscopeTabsState extends State { maxLines: 1, style: TextStyle( color: Colors.black, - fontSize: 9, + fontSize: 10, fontStyle: FontStyle.normal, fontWeight: FontWeight.bold, ), @@ -171,7 +171,7 @@ class _OscilloscopeTabsState extends State { maxLines: 1, style: TextStyle( color: Colors.black, - fontSize: 9, + fontSize: 10, fontStyle: FontStyle.normal, fontWeight: FontWeight.bold, ), @@ -222,7 +222,7 @@ class _OscilloscopeTabsState extends State { maxLines: 1, style: TextStyle( color: Colors.black, - fontSize: 9, + fontSize: 10, fontStyle: FontStyle.normal, fontWeight: FontWeight.bold, ), diff --git a/lib/view/widgets/timebase_trigger_widget.dart b/lib/view/widgets/timebase_trigger_widget.dart index 7f65ae91d..50b697ee5 100644 --- a/lib/view/widgets/timebase_trigger_widget.dart +++ b/lib/view/widgets/timebase_trigger_widget.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter_screenutil/flutter_screenutil.dart'; class TimebaseTriggerWidget extends StatefulWidget { const TimebaseTriggerWidget({super.key}); @@ -26,41 +25,35 @@ class _TimebaseTriggerState extends State { child: Stack( children: [ Positioned( - top: 0.h, - left: 2.w, - right: 0.w, + bottom: -4, + left: 4, + right: 0, child: Row( children: [ - Padding( - padding: EdgeInsets.only(bottom: 2.h), - child: Checkbox( - materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, - activeColor: const Color(0xFFCE525F), - value: isTriggerChecked, - onChanged: (bool? value) { - setState( - () { - isTriggerChecked = value; - }, - ); - }, - ), + Checkbox( + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + activeColor: const Color(0xFFCE525F), + value: isTriggerChecked, + onChanged: (bool? value) { + setState( + () { + isTriggerChecked = value; + }, + ); + }, ), - Padding( - padding: EdgeInsets.only(bottom: 2.h), - child: const Text( - 'Trigger', - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - fontStyle: FontStyle.normal, - ), + const Text( + 'Trigger', + style: TextStyle( + fontSize: 15, + fontWeight: FontWeight.bold, + fontStyle: FontStyle.normal, ), ), Padding( - padding: EdgeInsets.only(left: 8.w), + padding: const EdgeInsets.only(left: 8), child: DropdownMenu( - width: 50.w, + width: 90, initialSelection: 'CH1', dropdownMenuEntries: [ 'CH1', @@ -79,7 +72,7 @@ class _TimebaseTriggerState extends State { border: InputBorder.none, ), textStyle: const TextStyle( - fontSize: 14, + fontSize: 15, ), ), ), @@ -114,9 +107,9 @@ class _TimebaseTriggerState extends State { ), ), Padding( - padding: EdgeInsets.only(left: 8.w), + padding: const EdgeInsets.only(left: 32), child: DropdownMenu( - width: 70.w, + width: 150, initialSelection: 'Rising Edge', dropdownMenuEntries: [ 'Rising Edge', @@ -142,9 +135,9 @@ class _TimebaseTriggerState extends State { ), ), Positioned( - bottom: 0.h, - left: 8.w, - right: 8.w, + top: -2, + left: 16, + right: 16, child: Row( mainAxisSize: MainAxisSize.max, crossAxisAlignment: CrossAxisAlignment.center, @@ -152,7 +145,7 @@ class _TimebaseTriggerState extends State { const Text( 'Timebase', style: TextStyle( - fontSize: 14, + fontSize: 15, fontWeight: FontWeight.bold, fontStyle: FontStyle.normal, ), @@ -195,13 +188,13 @@ class _TimebaseTriggerState extends State { ), ), Positioned( - left: 0.w, - right: 0.w, - top: 2.h, + left: 0, + right: 0, + top: 1, child: Align( alignment: Alignment.center, child: Container( - padding: EdgeInsets.symmetric(horizontal: 2.w), + padding: const EdgeInsets.symmetric(horizontal: 2), decoration: const BoxDecoration(color: Colors.white), child: const Text( 'Timebase & Trigger', diff --git a/lib/view/widgets/xyplot_widget.dart b/lib/view/widgets/xyplot_widget.dart index d5d9082e6..8697257a2 100644 --- a/lib/view/widgets/xyplot_widget.dart +++ b/lib/view/widgets/xyplot_widget.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter_screenutil/flutter_screenutil.dart'; class XYPlotWidget extends StatefulWidget { const XYPlotWidget({super.key}); @@ -24,41 +23,44 @@ class _XYPlotState extends State { child: Stack( children: [ Positioned( - top: 0.h, - left: 2.w, - right: 0.w, + top: 4, + left: 4, + right: 0, child: Row( mainAxisSize: MainAxisSize.max, children: [ - Padding( - padding: EdgeInsets.only(bottom: 2.h), - child: Checkbox( - materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, - activeColor: const Color(0xFFCE525F), - value: isXYPlotSelected, - onChanged: (bool? value) { - setState( - () { - isXYPlotSelected = value; - }, - ); - }, - ), + Checkbox( + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + activeColor: const Color(0xFFCE525F), + value: isXYPlotSelected, + onChanged: (bool? value) { + setState( + () { + isXYPlotSelected = value; + }, + ); + }, ), - Padding( - padding: EdgeInsets.only(bottom: 2.h), - child: const Text( - 'Enable XY Plot', - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.normal, - fontStyle: FontStyle.normal, - ), + const Text( + 'Enable XY Plot', + style: TextStyle( + fontSize: 15, + fontWeight: FontWeight.normal, + fontStyle: FontStyle.normal, ), ), - const Spacer(), + ], + ), + ), + Positioned( + bottom: -6, + left: 12, + right: 12, + child: Row( + mainAxisSize: MainAxisSize.max, + children: [ DropdownMenu( - width: 50.w, + width: 90, initialSelection: 'CH1', dropdownMenuEntries: [ 'CH1', @@ -77,11 +79,11 @@ class _XYPlotState extends State { border: InputBorder.none, ), textStyle: const TextStyle( - fontSize: 14, + fontSize: 15, ), ), DropdownMenu( - width: 50.w, + width: 90, initialSelection: 'CH2', dropdownMenuEntries: [ 'CH1', @@ -100,23 +102,23 @@ class _XYPlotState extends State { border: InputBorder.none, ), textStyle: const TextStyle( - fontSize: 14, + fontSize: 15, ), ), ], ), - ), + ) ], ), ), Positioned( - left: 0.w, - right: 0.w, - top: 2.h, + left: 0, + right: 0, + top: 1, child: Align( alignment: Alignment.center, child: Container( - padding: EdgeInsets.symmetric(horizontal: 2.w), + padding: const EdgeInsets.symmetric(horizontal: 2), decoration: const BoxDecoration(color: Colors.white), child: const Text( 'XY Plot', diff --git a/pubspec.lock b/pubspec.lock index aa5f3f66e..0d364ae21 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -65,6 +65,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.8" + data: + dependency: "direct main" + description: + name: data + sha256: eb69f565061178da682b57e07468ff284e1156322ae48f0e7270e466943ea0df + url: "https://pub.dev" + source: hosted + version: "0.13.0" equatable: dependency: transitive description: @@ -93,15 +101,24 @@ packages: dependency: "direct main" description: name: fl_chart - sha256: "10ddaf334fe84d59333a12d153043e366f243e0bdfff2df0313e1e249f5bf926" + sha256: "5276944c6ffc975ae796569a826c38a62d2abcf264e26b88fa6f482e107f4237" url: "https://pub.dev" source: hosted - version: "0.70.1" + version: "0.70.2" flutter: dependency: "direct main" description: flutter source: sdk version: "0.0.0" + flutter_audio_capture: + dependency: "direct main" + description: + path: "." + ref: master + resolved-ref: "1cbaaa082d3158eb7c605789f48a3fc4e9eb3b14" + url: "https://github.com/AsCress/flutter_audio_capture.git" + source: git + version: "1.1.8" flutter_lints: dependency: "direct dev" description: @@ -110,14 +127,6 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.0" - flutter_screenutil: - dependency: "direct main" - description: - name: flutter_screenutil - sha256: "8239210dd68bee6b0577aa4a090890342d04a136ce1c81f98ee513fc0ce891de" - url: "https://pub.dev" - source: hosted - version: "5.9.3" flutter_svg: dependency: "direct main" description: @@ -151,10 +160,10 @@ packages: dependency: transitive description: name: http - sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010 + sha256: fe7ab022b76f3034adc518fb6ea04a82387620e19977665ea18d30a1cf43442f url: "https://pub.dev" source: hosted - version: "1.2.2" + version: "1.3.0" http_parser: dependency: transitive description: @@ -219,6 +228,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.15.0" + more: + dependency: transitive + description: + name: more + sha256: "7589f39d4e5858213e80454844c6f0b1ef58e254e6614fe387e2029fc325efad" + url: "https://pub.dev" + source: hosted + version: "4.4.0" nested: dependency: transitive description: @@ -315,14 +332,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.8" - polynomial: - dependency: "direct main" - description: - name: polynomial - sha256: "2a2e58e70a131a02d6073ff11d56dde8392d76d075ffdd038c067fc3e8258733" - url: "https://pub.dev" - source: hosted - version: "1.0.0" provider: dependency: "direct main" description: @@ -397,7 +406,7 @@ packages: description: path: "." ref: master - resolved-ref: "35f3cca1454f03f07bb646bfb36aed32011e5443" + resolved-ref: ba7892bca3b8bf7725af92c6cc58279659c2a2bc url: "https://github.com/AsCress/usbserial.git" source: git version: "0.5.1" @@ -466,5 +475,5 @@ packages: source: hosted version: "6.5.0" sdks: - dart: ">=3.6.0 <4.0.0" + dart: ">=3.5.4 <4.0.0" flutter: ">=3.24.0" diff --git a/pubspec.yaml b/pubspec.yaml index f0ae39912..676b42de1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -35,7 +35,6 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.8 - flutter_screenutil: ^5.9.3 flutter_svg: ^2.0.17 google_fonts: ^6.2.1 fl_chart: ^0.70.1 @@ -44,8 +43,12 @@ dependencies: git: url: https://github.com/AsCress/usbserial.git ref: master - polynomial: 1.0.0 get_it: ^8.0.3 + flutter_audio_capture: + git: + url: https://github.com/AsCress/flutter_audio_capture.git + ref: master + data: ^0.13.0 dev_dependencies: flutter_test: