Browse Source

添加心率检测算法

master
zhaohe 1 year ago
parent
commit
2a07091bdd
  1. 9
      .vscode/settings.json
  2. 222
      app/app.uvoptx
  3. 50
      app/app.uvprojx
  4. 2
      app/config/sdk_config.h
  5. 38
      app/src/basic/FIR.c
  6. 14
      app/src/basic/FIR.h
  7. 128
      app/src/basic/HC_Chen_detect.c
  8. 53
      app/src/basic/HC_Chen_detect.h
  9. 373
      app/src/basic/Pan_Tompkins_detect.c
  10. 21
      app/src/basic/Pan_Tompkins_detect.h
  11. 30
      app/src/basic/QRS.h
  12. 93
      app/src/basic/So_Chen_detect.c
  13. 42
      app/src/basic/So_Chen_detect.h
  14. 100
      app/src/basic/adaptive_algorithm.c
  15. 41
      app/src/basic/adaptive_algorithm.h
  16. 2
      app/src/board/board.h
  17. 4
      app/src/display_manager.c
  18. 146
      app/src/heart_wave_sample_service.c
  19. 1
      app/src/heart_wave_sample_service.h
  20. 2
      app/src/one_conduction_main.c
  21. 2
      ify_hrs_protocol
  22. 1
      tools/build.bat
  23. 121
      tools/one_lead_realtime_report_parser.cpp
  24. BIN
      tools/one_lead_realtime_report_parser.exe
  25. BIN
      tools/one_lead_upload_packet_parser.exe
  26. BIN
      tools/sample_bin_parse.exe
  27. BIN
      tools/text2bin.exe

9
.vscode/settings.json

@ -177,7 +177,14 @@
"xstring": "cpp",
"xtr1common": "cpp",
"xtree": "cpp",
"xutility": "cpp"
"xutility": "cpp",
"hc_chen_detect.h": "c",
"fir.h": "c",
"algorithm": "c",
"optional": "c",
"so_chen_detect.h": "c",
"adaptive_algorithm.h": "c",
"pan_tompkins_detect.h": "c"
},
"files.encoding": "gbk"
}

222
app/app.uvoptx

@ -655,6 +655,66 @@
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>1</GroupNumber>
<FileNumber>25</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>.\src\basic\FIR.c</PathWithFileName>
<FilenameWithoutPath>FIR.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>1</GroupNumber>
<FileNumber>26</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>.\src\basic\HC_Chen_detect.c</PathWithFileName>
<FilenameWithoutPath>HC_Chen_detect.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>1</GroupNumber>
<FileNumber>27</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>.\src\basic\So_Chen_detect.c</PathWithFileName>
<FilenameWithoutPath>So_Chen_detect.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>1</GroupNumber>
<FileNumber>28</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>.\src\basic\adaptive_algorithm.c</PathWithFileName>
<FilenameWithoutPath>adaptive_algorithm.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>1</GroupNumber>
<FileNumber>29</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>.\src\basic\Pan_Tompkins_detect.c</PathWithFileName>
<FilenameWithoutPath>Pan_Tompkins_detect.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
</Group>
<Group>
@ -665,7 +725,7 @@
<RteFlg>0</RteFlg>
<File>
<GroupNumber>2</GroupNumber>
<FileNumber>25</FileNumber>
<FileNumber>30</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -685,7 +745,7 @@
<RteFlg>0</RteFlg>
<File>
<GroupNumber>3</GroupNumber>
<FileNumber>26</FileNumber>
<FileNumber>31</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -697,7 +757,7 @@
</File>
<File>
<GroupNumber>3</GroupNumber>
<FileNumber>27</FileNumber>
<FileNumber>32</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -717,7 +777,7 @@
<RteFlg>0</RteFlg>
<File>
<GroupNumber>4</GroupNumber>
<FileNumber>28</FileNumber>
<FileNumber>33</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -737,7 +797,7 @@
<RteFlg>0</RteFlg>
<File>
<GroupNumber>5</GroupNumber>
<FileNumber>29</FileNumber>
<FileNumber>34</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -749,7 +809,7 @@
</File>
<File>
<GroupNumber>5</GroupNumber>
<FileNumber>30</FileNumber>
<FileNumber>35</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -761,7 +821,7 @@
</File>
<File>
<GroupNumber>5</GroupNumber>
<FileNumber>31</FileNumber>
<FileNumber>36</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -773,7 +833,7 @@
</File>
<File>
<GroupNumber>5</GroupNumber>
<FileNumber>32</FileNumber>
<FileNumber>37</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -785,7 +845,7 @@
</File>
<File>
<GroupNumber>5</GroupNumber>
<FileNumber>33</FileNumber>
<FileNumber>38</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -797,7 +857,7 @@
</File>
<File>
<GroupNumber>5</GroupNumber>
<FileNumber>34</FileNumber>
<FileNumber>39</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -809,7 +869,7 @@
</File>
<File>
<GroupNumber>5</GroupNumber>
<FileNumber>35</FileNumber>
<FileNumber>40</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -821,7 +881,7 @@
</File>
<File>
<GroupNumber>5</GroupNumber>
<FileNumber>36</FileNumber>
<FileNumber>41</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -841,7 +901,7 @@
<RteFlg>0</RteFlg>
<File>
<GroupNumber>6</GroupNumber>
<FileNumber>37</FileNumber>
<FileNumber>42</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -861,7 +921,7 @@
<RteFlg>0</RteFlg>
<File>
<GroupNumber>7</GroupNumber>
<FileNumber>38</FileNumber>
<FileNumber>43</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -873,7 +933,7 @@
</File>
<File>
<GroupNumber>7</GroupNumber>
<FileNumber>39</FileNumber>
<FileNumber>44</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -885,7 +945,7 @@
</File>
<File>
<GroupNumber>7</GroupNumber>
<FileNumber>40</FileNumber>
<FileNumber>45</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -897,7 +957,7 @@
</File>
<File>
<GroupNumber>7</GroupNumber>
<FileNumber>41</FileNumber>
<FileNumber>46</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -909,7 +969,7 @@
</File>
<File>
<GroupNumber>7</GroupNumber>
<FileNumber>42</FileNumber>
<FileNumber>47</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -921,7 +981,7 @@
</File>
<File>
<GroupNumber>7</GroupNumber>
<FileNumber>43</FileNumber>
<FileNumber>48</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -933,7 +993,7 @@
</File>
<File>
<GroupNumber>7</GroupNumber>
<FileNumber>44</FileNumber>
<FileNumber>49</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -945,7 +1005,7 @@
</File>
<File>
<GroupNumber>7</GroupNumber>
<FileNumber>45</FileNumber>
<FileNumber>50</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -957,7 +1017,7 @@
</File>
<File>
<GroupNumber>7</GroupNumber>
<FileNumber>46</FileNumber>
<FileNumber>51</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -969,7 +1029,7 @@
</File>
<File>
<GroupNumber>7</GroupNumber>
<FileNumber>47</FileNumber>
<FileNumber>52</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -981,7 +1041,7 @@
</File>
<File>
<GroupNumber>7</GroupNumber>
<FileNumber>48</FileNumber>
<FileNumber>53</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -993,7 +1053,7 @@
</File>
<File>
<GroupNumber>7</GroupNumber>
<FileNumber>49</FileNumber>
<FileNumber>54</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1005,7 +1065,7 @@
</File>
<File>
<GroupNumber>7</GroupNumber>
<FileNumber>50</FileNumber>
<FileNumber>55</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1017,7 +1077,7 @@
</File>
<File>
<GroupNumber>7</GroupNumber>
<FileNumber>51</FileNumber>
<FileNumber>56</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1029,7 +1089,7 @@
</File>
<File>
<GroupNumber>7</GroupNumber>
<FileNumber>52</FileNumber>
<FileNumber>57</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1041,7 +1101,7 @@
</File>
<File>
<GroupNumber>7</GroupNumber>
<FileNumber>53</FileNumber>
<FileNumber>58</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1053,7 +1113,7 @@
</File>
<File>
<GroupNumber>7</GroupNumber>
<FileNumber>54</FileNumber>
<FileNumber>59</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1065,7 +1125,7 @@
</File>
<File>
<GroupNumber>7</GroupNumber>
<FileNumber>55</FileNumber>
<FileNumber>60</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1077,7 +1137,7 @@
</File>
<File>
<GroupNumber>7</GroupNumber>
<FileNumber>56</FileNumber>
<FileNumber>61</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1089,7 +1149,7 @@
</File>
<File>
<GroupNumber>7</GroupNumber>
<FileNumber>57</FileNumber>
<FileNumber>62</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1109,7 +1169,7 @@
<RteFlg>0</RteFlg>
<File>
<GroupNumber>8</GroupNumber>
<FileNumber>58</FileNumber>
<FileNumber>63</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1121,7 +1181,7 @@
</File>
<File>
<GroupNumber>8</GroupNumber>
<FileNumber>59</FileNumber>
<FileNumber>64</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1133,7 +1193,7 @@
</File>
<File>
<GroupNumber>8</GroupNumber>
<FileNumber>60</FileNumber>
<FileNumber>65</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1145,7 +1205,7 @@
</File>
<File>
<GroupNumber>8</GroupNumber>
<FileNumber>61</FileNumber>
<FileNumber>66</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1157,7 +1217,7 @@
</File>
<File>
<GroupNumber>8</GroupNumber>
<FileNumber>62</FileNumber>
<FileNumber>67</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1169,7 +1229,7 @@
</File>
<File>
<GroupNumber>8</GroupNumber>
<FileNumber>63</FileNumber>
<FileNumber>68</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1181,7 +1241,7 @@
</File>
<File>
<GroupNumber>8</GroupNumber>
<FileNumber>64</FileNumber>
<FileNumber>69</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1193,7 +1253,7 @@
</File>
<File>
<GroupNumber>8</GroupNumber>
<FileNumber>65</FileNumber>
<FileNumber>70</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1205,7 +1265,7 @@
</File>
<File>
<GroupNumber>8</GroupNumber>
<FileNumber>66</FileNumber>
<FileNumber>71</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1217,7 +1277,7 @@
</File>
<File>
<GroupNumber>8</GroupNumber>
<FileNumber>67</FileNumber>
<FileNumber>72</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1229,7 +1289,7 @@
</File>
<File>
<GroupNumber>8</GroupNumber>
<FileNumber>68</FileNumber>
<FileNumber>73</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1241,7 +1301,7 @@
</File>
<File>
<GroupNumber>8</GroupNumber>
<FileNumber>69</FileNumber>
<FileNumber>74</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1253,7 +1313,7 @@
</File>
<File>
<GroupNumber>8</GroupNumber>
<FileNumber>70</FileNumber>
<FileNumber>75</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1265,7 +1325,7 @@
</File>
<File>
<GroupNumber>8</GroupNumber>
<FileNumber>71</FileNumber>
<FileNumber>76</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1277,7 +1337,7 @@
</File>
<File>
<GroupNumber>8</GroupNumber>
<FileNumber>72</FileNumber>
<FileNumber>77</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1289,7 +1349,7 @@
</File>
<File>
<GroupNumber>8</GroupNumber>
<FileNumber>73</FileNumber>
<FileNumber>78</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1301,7 +1361,7 @@
</File>
<File>
<GroupNumber>8</GroupNumber>
<FileNumber>74</FileNumber>
<FileNumber>79</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1313,7 +1373,7 @@
</File>
<File>
<GroupNumber>8</GroupNumber>
<FileNumber>75</FileNumber>
<FileNumber>80</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1325,7 +1385,7 @@
</File>
<File>
<GroupNumber>8</GroupNumber>
<FileNumber>76</FileNumber>
<FileNumber>81</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1337,7 +1397,7 @@
</File>
<File>
<GroupNumber>8</GroupNumber>
<FileNumber>77</FileNumber>
<FileNumber>82</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1349,7 +1409,7 @@
</File>
<File>
<GroupNumber>8</GroupNumber>
<FileNumber>78</FileNumber>
<FileNumber>83</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1361,7 +1421,7 @@
</File>
<File>
<GroupNumber>8</GroupNumber>
<FileNumber>79</FileNumber>
<FileNumber>84</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1373,7 +1433,7 @@
</File>
<File>
<GroupNumber>8</GroupNumber>
<FileNumber>80</FileNumber>
<FileNumber>85</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1385,7 +1445,7 @@
</File>
<File>
<GroupNumber>8</GroupNumber>
<FileNumber>81</FileNumber>
<FileNumber>86</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1397,7 +1457,7 @@
</File>
<File>
<GroupNumber>8</GroupNumber>
<FileNumber>82</FileNumber>
<FileNumber>87</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1409,7 +1469,7 @@
</File>
<File>
<GroupNumber>8</GroupNumber>
<FileNumber>83</FileNumber>
<FileNumber>88</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1421,7 +1481,7 @@
</File>
<File>
<GroupNumber>8</GroupNumber>
<FileNumber>84</FileNumber>
<FileNumber>89</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1441,7 +1501,7 @@
<RteFlg>0</RteFlg>
<File>
<GroupNumber>9</GroupNumber>
<FileNumber>85</FileNumber>
<FileNumber>90</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1453,7 +1513,7 @@
</File>
<File>
<GroupNumber>9</GroupNumber>
<FileNumber>86</FileNumber>
<FileNumber>91</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1465,7 +1525,7 @@
</File>
<File>
<GroupNumber>9</GroupNumber>
<FileNumber>87</FileNumber>
<FileNumber>92</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1477,7 +1537,7 @@
</File>
<File>
<GroupNumber>9</GroupNumber>
<FileNumber>88</FileNumber>
<FileNumber>93</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1489,7 +1549,7 @@
</File>
<File>
<GroupNumber>9</GroupNumber>
<FileNumber>89</FileNumber>
<FileNumber>94</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1501,7 +1561,7 @@
</File>
<File>
<GroupNumber>9</GroupNumber>
<FileNumber>90</FileNumber>
<FileNumber>95</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1521,7 +1581,7 @@
<RteFlg>0</RteFlg>
<File>
<GroupNumber>10</GroupNumber>
<FileNumber>91</FileNumber>
<FileNumber>96</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1533,7 +1593,7 @@
</File>
<File>
<GroupNumber>10</GroupNumber>
<FileNumber>92</FileNumber>
<FileNumber>97</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1545,7 +1605,7 @@
</File>
<File>
<GroupNumber>10</GroupNumber>
<FileNumber>93</FileNumber>
<FileNumber>98</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1565,7 +1625,7 @@
<RteFlg>0</RteFlg>
<File>
<GroupNumber>11</GroupNumber>
<FileNumber>94</FileNumber>
<FileNumber>99</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1577,7 +1637,7 @@
</File>
<File>
<GroupNumber>11</GroupNumber>
<FileNumber>95</FileNumber>
<FileNumber>100</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1589,7 +1649,7 @@
</File>
<File>
<GroupNumber>11</GroupNumber>
<FileNumber>96</FileNumber>
<FileNumber>101</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1609,7 +1669,7 @@
<RteFlg>0</RteFlg>
<File>
<GroupNumber>12</GroupNumber>
<FileNumber>97</FileNumber>
<FileNumber>102</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1621,7 +1681,7 @@
</File>
<File>
<GroupNumber>12</GroupNumber>
<FileNumber>98</FileNumber>
<FileNumber>103</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1633,7 +1693,7 @@
</File>
<File>
<GroupNumber>12</GroupNumber>
<FileNumber>99</FileNumber>
<FileNumber>104</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1653,7 +1713,7 @@
<RteFlg>0</RteFlg>
<File>
<GroupNumber>13</GroupNumber>
<FileNumber>100</FileNumber>
<FileNumber>105</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1665,7 +1725,7 @@
</File>
<File>
<GroupNumber>13</GroupNumber>
<FileNumber>101</FileNumber>
<FileNumber>106</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1685,7 +1745,7 @@
<RteFlg>0</RteFlg>
<File>
<GroupNumber>14</GroupNumber>
<FileNumber>102</FileNumber>
<FileNumber>107</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1697,7 +1757,7 @@
</File>
<File>
<GroupNumber>14</GroupNumber>
<FileNumber>103</FileNumber>
<FileNumber>108</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1709,7 +1769,7 @@
</File>
<File>
<GroupNumber>14</GroupNumber>
<FileNumber>104</FileNumber>
<FileNumber>109</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1721,7 +1781,7 @@
</File>
<File>
<GroupNumber>14</GroupNumber>
<FileNumber>105</FileNumber>
<FileNumber>110</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>

50
app/app.uvprojx

@ -503,6 +503,31 @@
<FileType>1</FileType>
<FilePath>.\src\board\board_light_ctrl.c</FilePath>
</File>
<File>
<FileName>FIR.c</FileName>
<FileType>1</FileType>
<FilePath>.\src\basic\FIR.c</FilePath>
</File>
<File>
<FileName>HC_Chen_detect.c</FileName>
<FileType>1</FileType>
<FilePath>.\src\basic\HC_Chen_detect.c</FilePath>
</File>
<File>
<FileName>So_Chen_detect.c</FileName>
<FileType>1</FileType>
<FilePath>.\src\basic\So_Chen_detect.c</FilePath>
</File>
<File>
<FileName>adaptive_algorithm.c</FileName>
<FileType>1</FileType>
<FilePath>.\src\basic\adaptive_algorithm.c</FilePath>
</File>
<File>
<FileName>Pan_Tompkins_detect.c</FileName>
<FileType>1</FileType>
<FilePath>.\src\basic\Pan_Tompkins_detect.c</FilePath>
</File>
</Files>
</Group>
<Group>
@ -4387,6 +4412,31 @@
<FileType>1</FileType>
<FilePath>.\src\board\board_light_ctrl.c</FilePath>
</File>
<File>
<FileName>FIR.c</FileName>
<FileType>1</FileType>
<FilePath>.\src\basic\FIR.c</FilePath>
</File>
<File>
<FileName>HC_Chen_detect.c</FileName>
<FileType>1</FileType>
<FilePath>.\src\basic\HC_Chen_detect.c</FilePath>
</File>
<File>
<FileName>So_Chen_detect.c</FileName>
<FileType>1</FileType>
<FilePath>.\src\basic\So_Chen_detect.c</FilePath>
</File>
<File>
<FileName>adaptive_algorithm.c</FileName>
<FileType>1</FileType>
<FilePath>.\src\basic\adaptive_algorithm.c</FilePath>
</File>
<File>
<FileName>Pan_Tompkins_detect.c</FileName>
<FileType>1</FileType>
<FilePath>.\src\basic\Pan_Tompkins_detect.c</FilePath>
</File>
</Files>
</Group>
<Group>

2
app/config/sdk_config.h

@ -7812,7 +7812,7 @@
// <e> NRF_LOG_ENABLED - nrf_log - Logger
//==========================================================
#ifndef NRF_LOG_ENABLED
#define NRF_LOG_ENABLED 0
#define NRF_LOG_ENABLED 1
#endif
// <h> Log message pool - Configuration of log message pool

38
app/src/basic/FIR.c

@ -0,0 +1,38 @@
#include "FIR.h"
/*360hz 0.51Hz~8.9Hz 20190925*/
#define taps 32
static const float coefficients[taps] = {0.012177,0.01599,0.019905,0.02387,0.027827,0.031719,0.035487,0.039075,0.042426,0.045488,0.048212,0.050553,0.052475,0.053944,0.054937,0.055438,0.055438,0.054937,0.053944,0.052475,0.050553,0.048212,0.045488,0.042426,0.039075,0.035487,0.031719,0.027827,0.02387,0.019905,0.01599,0.012177};
static float buffer[taps];
unsigned offset;
float FIR_filter(float input) {
const float *coeff = coefficients;
const float *coeff_end = coefficients + taps;
float *buf_val = buffer + offset;
*buf_val = input;
float output_ = 0;
while (buf_val >= buffer) {
output_ += *buf_val-- * *coeff++;
}
buf_val = buffer + taps - 1;
while (coeff < coeff_end) {
output_ += *buf_val-- * *coeff++;
}
if (++offset >= taps) {
offset = 0;
}
return output_;
}
void FIR_reset_buffer() {
memset(buffer, 0, sizeof(float) * taps);
offset = 0;
}

14
app/src/basic/FIR.h

@ -0,0 +1,14 @@
#ifndef __FIR_H__
#define __FIR_H__
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <math.h>
extern float FIR_filter(float);
extern void FIR_reset_buffer();
#endif

128
app/src/basic/HC_Chen_detect.c

@ -0,0 +1,128 @@
#include "HC_Chen_detect.h"
bool HC_Chen_detect(float signal)
{
ecg_buff[ecg_buff_WR_idx++] = signal;
sample = ecg_buff_WR_idx;
ecg_buff_WR_idx %= (M+1);
/* High pass filtering */
if(number_iter < M){
// first fill buffer with enough points for HP filter
hp_sum += ecg_buff[ecg_buff_RD_idx];
hp_buff[hp_buff_WR_idx] = 0;
}
else{
hp_sum += ecg_buff[ecg_buff_RD_idx];
int tmp = ecg_buff_RD_idx - M;
if(tmp < 0){
tmp += M + 1;
}
hp_sum -= ecg_buff[tmp];
float y1 = 0;
float y2 = 0;
tmp = (ecg_buff_RD_idx - ((M+1)/2));
if(tmp < 0){
tmp += M + 1;
}
y2 = ecg_buff[tmp];
y1 = HP_CONSTANT * hp_sum;
hp_buff[hp_buff_WR_idx] = y2 - y1;
}
// done reading ECG buffer, increment position
ecg_buff_RD_idx++;
ecg_buff_RD_idx %= (M+1);
// done writing to HP buffer, increment position
hp_buff_WR_idx++;
hp_buff_WR_idx %= (N+1);
/* Low pass filtering */
// shift in new sample from high pass filter
lp_sum += hp_buff[hp_buff_RD_idx] * hp_buff[hp_buff_RD_idx];
if(number_iter < N){
// first fill buffer with enough points for LP filter
next_eval_pt = 0;
}
else{
// shift out oldest data point
int tmp = hp_buff_RD_idx - N;
if(tmp < 0){
tmp += N+1;
}
lp_sum -= hp_buff[tmp] * hp_buff[tmp];
next_eval_pt = lp_sum;
}
// done reading HP buffer, increment position
hp_buff_RD_idx++;
hp_buff_RD_idx %= (N+1);
/* Adapative thresholding beat detection */
// set initial threshold
if(number_iter < window_size) {
if(next_eval_pt > treshold) {
treshold = next_eval_pt;
}
++number_iter;
}
// check if detection hold off period has passed
if(triggered){
trig_time++;
if(trig_time >= DELAY_TIME){
triggered = false;
trig_time = 0;
}
}
// find if we have a new max
if(next_eval_pt > win_max) win_max = next_eval_pt;
// find if we are above adaptive threshold
if(next_eval_pt > treshold && !triggered) {
//result.push_back(true);
last_qrs_point = sample;
triggered = true;
return true;
}
else {
//result.push_back(false);
}
// adjust adaptive threshold using max of signal found
// in previous window
if(win_idx++ >= window_size){
// weighting factor for determining the contribution of
// the current peak value to the threshold adjustment
float gamma = (0.2f+0.15f)/2.0f; // 0.15~0.2
// forgetting factor -
// rate at which we forget old observations
float alpha = 0.01f + ( ((float) rand() / (float) RAND_MAX) * ((0.1f - 0.01f))); // 0~1
//float alpha = 1.0f*exp(-0.00005f*(sample - last_qrs_point));
treshold = alpha * gamma * win_max + (1.0f - alpha) * treshold;
// reset current window ind
win_idx = 0;
win_max = -10000000;
}
return false;
}

53
app/src/basic/HC_Chen_detect.h

@ -0,0 +1,53 @@
#ifndef __HC_CHEN__
#define __HC_CHEN__
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <math.h>
#include <stdint.h>
#include "QRS.h"
#define M 9
#define N 54//SAMPLING_RATE * 0.15f
static const uint32_t window_size = SAMPLING_RATE;
static const float HP_CONSTANT = ((float) 1.0f / (float) M);
// circular buffer for input ecg signal
// we need to keep a history of M + 1 samples for HP filter
static float ecg_buff[M + 1] = {0};
static int ecg_buff_WR_idx = 0;
static int ecg_buff_RD_idx = 0;
// circular buffer for input ecg signal
// we need to keep a history of N+1 samples for LP filter
static float hp_buff[N + 1] = {0};
static int hp_buff_WR_idx = 0;
static int hp_buff_RD_idx = 0;
// LP filter outputs a single point for every input point
// This goes straight to adaptive filtering for eval
static float next_eval_pt = 0;
// running sums for HP and LP filters, values shifted in FILO
static float hp_sum = 0;
static float lp_sum = 0;
// parameters for adaptive thresholding
static float treshold = 0;
static bool triggered = false;
static int trig_time = 0;
static float win_max = 0;
static int win_idx = 0;
static int number_iter = 0;
static int sample = 0;
static int last_qrs_point = 0;
static const int DELAY_TIME = 180;//window_size * 0.5f;
extern bool HC_Chen_detect(float);
#endif

373
app/src/basic/Pan_Tompkins_detect.c

@ -0,0 +1,373 @@
#include "Pan_Tompkins_detect.h"
/* y(nT) = 1.875y(nT – T) – 0.9219y(nT – 2T) + x (nT) – x(nT – 2T) */
int TwoPoleRecursive(int data)
{
static int xnt, xm1, xm2, ynt, ym1, ym2 = 0;
xnt = data;
ynt = (ym1 + (ym1 >> 1) + (ym1 >> 2) + (ym1 >> 3)) + // 1.875 = 1 + 1/2 + 1/4 + 1/8
(((ym2 >> 1) + (ym2 >> 2) + (ym2 >> 3) + (ym2 >> 5) + (ym2 >> 6)) + xnt - xm2); // 0.916 = 1 + 1/2 + 1/4 + 1/8 + 1/32 + 1/64
xm2 = xm1;
xm1 = xnt;
xm2 = ym1;
ym2 = ym1;
ym1 = ynt;
return ynt;
}
/* y(nT) = 2y(nT – T) – y(nT – 2T) + x(nT) – 2x(nT – 6T) + x(nT – 12T) */
int LowPassFilter(int data)
{
static int y1 = 0, y2 = 0, x[26], n = 12;
int y0;
x[n] = x[n + 13] = data;
y0 = (y1 << 1) - y2 + x[n] - (x[n + 6] << 1) + x[n + 12];
y2 = y1;
y1 = y0;
y0 >>= 5;
if(--n < 0){
n = 12;
}
return y0;
}
/* p(nT) = x(nT – 16T) – 32 [y(nT – T) + x(nT) – x(nT – 32T)] */
int HighPassFilter(int data)
{
static int y1 = 0, x[66], n = 32;
int y0;
x[n] = x[n + 33] = data;
y0 = y1 + x[n] - x[n + 32];
y1 = y0;
if(--n < 0){
n = 32;
}
return (x[n + 16] - (y0 >> 5));
}
/* y = 1/8 (2x( nT) + x( nT - T) - x( nT - 3T) - 2x( nT - 4T)) */
int Derivative(int data)
{
int y;
static int x_derv[4];
y = (data << 1) + x_derv[3] - x_derv[1] - ( x_derv[0] << 1);
y >>= 3;
for(int i = 0; i < 3; ++i){
x_derv[i] = x_derv[i + 1];
}
x_derv[3] = data;
return y;
}
int Squar(int data)
{
return (data * data);
}
/* y(nT) = 1/N [x(nT – (N – 1)T) + x(nT – (N – 2)T) +...+ x(nT)] */
int MovingWindowIntegral(int data)
{
//static const int WINDOW_SIZE = SAMPLING_RATE * 0.2;
#define WINDOW_SIZE 72
static int x[WINDOW_SIZE], ptr = 0;
static long sum = 0;
long ly;
int y;
if(++ptr == WINDOW_SIZE){
ptr = 0;
}
sum -= x[ptr];
sum += data;
x[ptr] = data;
ly = sum >> 5;
uint32_t MAX_INTEGRAL = 4096;//32400;
if(ly > MAX_INTEGRAL){
y = MAX_INTEGRAL;
}
else{
y = (int)ly;
}
return (y);
}
SignalPoint ThresholdCalculate(int sample,float value,int bandpass,int square,int integral)
{
//static const int QRS_TIME = SAMPLING_RATE * 0.1;
//static const int SEARCH_BACK_TIME = SAMPLING_RATE * 1.66f;
#define QRS_TIME 36
#define SEARCH_BACK_TIME 598
static int bandpass_buffer[SEARCH_BACK_TIME],integral_buffer[SEARCH_BACK_TIME];
static SignalPoint peak_buffer[SEARCH_BACK_TIME];
static int square_buffer[QRS_TIME];
static long unsigned last_qrs = 0, last_slope = 0, current_slope = 0;
static int peak_i = 0, peak_f = 0, threshold_i1 = 0, threshold_i2 = 0, threshold_f1 = 0, threshold_f2 = 0, spk_i = 0, spk_f = 0, npk_i = 0, npk_f = 0;
static bool qrs, regular = true, prev_regular;
static int rr1[8]={0}, rr2[8]={0}, rravg1, rravg2, rrlow = 0, rrhigh = 0, rrmiss = 0;
SignalPoint result;
result.index = -1;
peak_buffer[sample%SEARCH_BACK_TIME].index = sample;
peak_buffer[sample%SEARCH_BACK_TIME].value = value;
bandpass_buffer[sample%SEARCH_BACK_TIME] = bandpass;
integral_buffer[sample%SEARCH_BACK_TIME] = integral;
square_buffer[sample%QRS_TIME] = square;
// If the current signal is above one of the thresholds (integral or filtered signal), it's a peak candidate.
if(integral >= threshold_i1 || bandpass >= threshold_f1){
peak_i = integral;
peak_f = bandpass;
}
// If both the integral and the signal are above their thresholds, they're probably signal peaks.
if((integral >= threshold_i1) && (bandpass >= threshold_f1)){
// There's a 200ms latency. If the new peak respects this condition, we can keep testing.
if(sample > last_qrs + SAMPLING_RATE*0.2f){
//if(sample > last_qrs + (SAMPLING_RATE*0.2f)){
// If it respects the 200ms latency, but it doesn't respect the 360ms latency, we check the slope.
if(sample <= last_qrs + (long unsigned int)(0.36*SAMPLING_RATE)){
// The squared slope is "M" shaped. So we have to check nearby samples to make sure we're really looking
// at its peak value, rather than a low one.
int current = sample;
current_slope = 0;
for(int j = current - QRS_TIME; j <= current; ++j){
if(square_buffer[j%QRS_TIME] > current_slope){
current_slope = square_buffer[j%QRS_TIME];
}
}
//current_slope = square;
if(current_slope <= (int)(last_slope/2)){
qrs = false;
//return qrs;
}
else{
spk_i = 0.125*peak_i + 0.875*spk_i;
threshold_i1 = npk_i + 0.25*(spk_i - npk_i);
threshold_i2 = 0.5*threshold_i1;
spk_f = 0.125*peak_f + 0.875*spk_f;
threshold_f1 = npk_f + 0.25*(spk_f - npk_f);
threshold_f2 = 0.5*threshold_f1;
last_slope = current_slope;
qrs = true;
result.value = value;
result.index = sample;
}
}
// If it was above both thresholds and respects both latency periods, it certainly is a R peak.
else{
int current = sample;
current_slope = 0;
for(int j = current - QRS_TIME; j <= current; ++j){
if(square_buffer[j%QRS_TIME] > current_slope){
current_slope = square_buffer[j%QRS_TIME];
}
}
//current_slope = square;
spk_i = 0.125*peak_i + 0.875*spk_i;
threshold_i1 = npk_i + 0.25*(spk_i - npk_i);
threshold_i2 = 0.5*threshold_i1;
spk_f = 0.125*peak_f + 0.875*spk_f;
threshold_f1 = npk_f + 0.25*(spk_f - npk_f);
threshold_f2 = 0.5*threshold_f1;
last_slope = current_slope;
qrs = true;
result.value = value;
result.index = sample;
}
}
// If the new peak doesn't respect the 200ms latency, it's noise. Update thresholds and move on to the next sample.
else{
peak_i = integral;
npk_i = 0.125*peak_i + 0.875*npk_i;
threshold_i1 = npk_i + 0.25*(spk_i - npk_i);
threshold_i2 = 0.5*threshold_i1;
peak_f = bandpass;
npk_f = 0.125*peak_f + 0.875*npk_f;
threshold_f1 = npk_f + 0.25*(spk_f - npk_f);
threshold_f2 = 0.5*threshold_f1;
qrs = false;
/*outputSignal[current] = qrs;
if (sample > DELAY + BUFFSIZE)
output(outputSignal[0]);
continue;*/
//return qrs;
return result;
}
}
// If a QRS complex was detected, the RR-averages must be updated.
if(qrs){
// Add the newest RR-interval to the buffer and get the new average.
rravg1 = 0;
for (int i = 0; i < 7; ++i){
rr1[i] = rr1[i+1];
rravg1 += rr1[i];
}
rr1[7] = sample - last_qrs;
last_qrs = sample;
rravg1 += rr1[7];
rravg1 *= 0.125;
// If the newly-discovered RR-average is normal, add it to the "normal" buffer and get the new "normal" average.
// Update the "normal" beat parameters.
if ( (rr1[7] >= rrlow) && (rr1[7] <= rrhigh) ){
rravg2 = 0;
for (int i = 0; i < 7; ++i){
rr2[i] = rr2[i+1];
rravg2 += rr2[i];
}
rr2[7] = rr1[7];
rravg2 += rr2[7];
rravg2 *= 0.125;
rrlow = 0.92*rravg2;
rrhigh = 1.16*rravg2;
rrmiss = 1.66*rravg2;
}
prev_regular = regular;
if(rravg1 == rravg2){
regular = true;
}
// If the beat had been normal but turned odd, change the thresholds.
else{
regular = false;
if (prev_regular){
threshold_i1 /= 2;
threshold_f1 /= 2;
}
}
}
// If no R-peak was detected, it's important to check how long it's been since the last detection.
else{
int current = sample;
// If no R-peak was detected for too long, use the lighter thresholds and do a back search.
// However, the back search must respect the 200ms limit and the 360ms one (check the slope).
if((sample - last_qrs > (long unsigned int)rrmiss) && (sample > last_qrs + SAMPLING_RATE*0.2f)){
//if((sample - last_qrs > (long unsigned int)rrmiss) && (sample > last_qrs + (SAMPLING_RATE*0.2f))){
// If over SEARCH_BACK_TIME of QRS complex
if((sample - last_qrs) > SEARCH_BACK_TIME){
last_qrs = sample;
//return result;
}
int qrs_last_index = 0; // Last point of QRS complex
for(int i = current - (sample - last_qrs) + SAMPLING_RATE*0.2f; i < (long unsigned int)current; ++i){
//for(int i = current - (sample - last_qrs) + (SAMPLING_RATE*0.2f); i < (long unsigned int)current; ++i){
if((integral_buffer[i%SEARCH_BACK_TIME] > threshold_i2) && (bandpass_buffer[i%SEARCH_BACK_TIME] > threshold_f2)){
current_slope = 0;
for(int j = current - QRS_TIME; j <= current; ++j){
if(square_buffer[j%QRS_TIME] > current_slope){
current_slope = square_buffer[j%QRS_TIME];
}
}
//current_slope = square;
if((current_slope < (int)(last_slope/2)) && (i + sample) < last_qrs + 0.36*last_qrs){
qrs = false;
}
else if(i - last_qrs > 550){
peak_i = integral_buffer[i%SEARCH_BACK_TIME];
peak_f = bandpass_buffer[i%SEARCH_BACK_TIME];
spk_i = 0.25*peak_i+ 0.75*spk_i;
spk_f = 0.25*peak_f + 0.75*spk_f;
threshold_i1 = npk_i + 0.25*(spk_i - npk_i);
threshold_i2 = 0.5*threshold_i1;
last_slope = current_slope;
threshold_f1 = npk_f + 0.25*(spk_f - npk_f);
threshold_f2 = 0.5*threshold_f1;
// If a signal peak was detected on the back search, the RR attributes must be updated.
// This is the same thing done when a peak is detected on the first try.
//RR Average 1
rravg1 = 0;
for(int j = 0; j < 7; ++j){
rr1[j] = rr1[j+1];
rravg1 += rr1[j];
}
rr1[7] = sample - (current - i) - last_qrs;
qrs = true;
qrs_last_index = i;
last_qrs = sample - (current - i);
rravg1 += rr1[7];
rravg1 *= 0.125;
//RR Average 2
if((rr1[7] >= rrlow) && (rr1[7] <= rrhigh)){
rravg2 = 0;
for (int i = 0; i < 7; ++i){
rr2[i] = rr2[i+1];
rravg2 += rr2[i];
}
rr2[7] = rr1[7];
rravg2 += rr2[7];
rravg2 *= 0.125;
rrlow = 0.92*rravg2;
rrhigh = 1.16*rravg2;
rrmiss = 1.66*rravg2;
}
prev_regular = regular;
if(rravg1 == rravg2){
regular = true;
}
else{
regular = false;
if(prev_regular){
threshold_i1 /= 2;
threshold_f1 /= 2;
}
}
break;
}
}
}
if(qrs){
//outputSignal[current] = false;
//outputSignal[i] = true;
//if (sample > DELAY + BUFFSIZE)
//output(outputSignal[0]);
//continue;
//return qrs;
return peak_buffer[qrs_last_index%SEARCH_BACK_TIME];
}
}
// Definitely no signal peak was detected.
if(!qrs){
// If some kind of peak had been detected, then it's certainly a noise peak. Thresholds must be updated accordinly.
if((integral >= threshold_i1) || (bandpass >= threshold_f1)){
peak_i = integral;
npk_i = 0.125*peak_i + 0.875*npk_i;
threshold_i1 = npk_i + 0.25*(spk_i - npk_i);
threshold_i2 = 0.5*threshold_i1;
peak_f = bandpass;
npk_f = 0.125*peak_f + 0.875*npk_f;
threshold_f1 = npk_f + 0.25*(spk_f - npk_f);
threshold_f2 = 0.5*threshold_f1;
}
}
}
return result;
}

21
app/src/basic/Pan_Tompkins_detect.h

@ -0,0 +1,21 @@
#ifndef __PAN_TOMPKINS__
#define __PAN_TOMPKINS__
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <math.h>
#include "QRS.h"
extern int TwoPoleRecursive(int);
extern int LowPassFilter(int);
extern int HighPassFilter(int);
extern int Derivative(int);
extern int Squar(int);
extern int MovingWindowIntegral(int);
extern SignalPoint ThresholdCalculate(int,float,int,int,int);
#endif

30
app/src/basic/QRS.h

@ -0,0 +1,30 @@
#pragma once
#include <stdint.h>
//const uint32_t SAMPLING_RATE = 1000;
#define SAMPLING_RATE 360
typedef struct
{
float value;
int32_t index;
}SignalPoint;
enum
{
NOTQRS, /* not-QRS (not a getann/putann code) */
NORMAL, /* normal beat */
LBBB, /* left bundle branch block beat */
RBBB, /* right bundle branch block beat */
ABERR, /* aberrated atrial premature beat */
PVC, /* premature ventricular contraction */
FUSION, /* fusion of ventricular and normal beat */
NPC, /* nodal (junctional) premature beat */
APC, /* atrial premature contraction */
SVPB, /* premature or ectopic supraventricular beat */
VESC, /* ventricular escape beat */
NESC, /* nodal (junctional) escape beat */
PACE, /* paced beat */
UNKNOWN, /* unclassifiable beat */
NOISE, /* signal quality change */
ARFCT /* isolated QRS-like artifact */
};

93
app/src/basic/So_Chen_detect.c

@ -0,0 +1,93 @@
#include "So_Chen_detect.h"
SignalPoint So_Chen_detect(SignalPoint signal,int initial_point,float threshold_parameter,float filter_parameter)
{
/* init slop window pool, size = 5 */
if(signal_window_count < signal_window_size){
signal_window[signal_window_count%signal_window_size] = signal;
++signal_window_count;
SignalPoint value;
value.index = -1;
return value;
}
else{
signal_window[signal_window_count%signal_window_size] = signal;
++signal_window_count;
SignalPoint value;
}
/* calculate slop */
uint32_t idx_for_slop = signal_window_count-2;
slop.value = ( (-2.0f * signal_window[(idx_for_slop-2)%signal_window_size].value) - signal_window[(idx_for_slop-1)%signal_window_size].value + signal_window[(idx_for_slop+1)%signal_window_size].value + (2.0f * signal_window[(idx_for_slop+2)%signal_window_size].value) );
slop.index = signal_window[idx_for_slop%signal_window_size].index;
/* init maxi */
if(!so_chen_init_flag){
if(!maxi_init){
max.value = 0;
max.index = -1;
maxi = slop.value;
maxi_init = true;
}
++init_count;
if(init_count > initial_point){
so_chen_init_flag = true;
/* calculate slop threshold */
slop_threshold = threshold_parameter / 16.0f * maxi;
}
if(slop.value > maxi){
maxi = slop.value;
}
SignalPoint value;
value.index = -1;
return value;
}
/* detect QRS complex on set */
if(qrs_on_set_flag && (signal_window_count - last_point > enhanced_point)){
if(!max_init){
max = signal_window[(idx_for_slop)%signal_window_size];
max_init = true;
}
if(signal_window[(idx_for_slop)%signal_window_size].value > max.value){
max = signal_window[(idx_for_slop)%signal_window_size];
max_slop = slop;
}
else if(signal_window[(idx_for_slop)%signal_window_size].value < max.value){
last_point = signal_window_count;
qrs_on_set_flag = false;
max_init = false;
maxi = ((abs(max.value - qrs_onset_point.value) - maxi) / filter_parameter) + maxi;
slop_threshold = threshold_parameter / 16.0f * maxi;
last_maxi = maxi;
return max;
}
}
else{
if(slop.value > slop_threshold){
++qrs_on_set_count;
}
else if(qrs_on_set_count){
qrs_on_set_count = 0;
}
if(qrs_on_set_count >= 2){ // is QRS complex on set
qrs_on_set_flag = true;
qrs_on_set_count = 0;
qrs_onset_idx = idx_for_slop;
qrs_onset_point = signal;
}
else if((signal_window_count - last_point > enhanced_point * 2) && (slop_threshold > 0)){ //decay threshold
slop_threshold -= slop.value;
if((signal_window_count - last_point > SAMPLING_RATE * 3)){ //threshold oscillating
slop_threshold -= ((signal_window_count - last_point) >> (int)threshold_parameter);
}
}
}
SignalPoint value;
value.index = -1;
return value;
}

42
app/src/basic/So_Chen_detect.h

@ -0,0 +1,42 @@
#ifndef __SO_AND_CHEN__
#define __SO_AND_CHEN__
#include <stdlib.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdint.h>
#include <math.h>
#include "QRS.h"
static const uint32_t enhanced_point = SAMPLING_RATE * 0.35f;
#define signal_window_size 5
static SignalPoint signal_window[signal_window_size];
static uint32_t signal_window_count = 0;
static SignalPoint slop;
static bool so_chen_init_flag = false;
static uint32_t init_count = 0;
static bool maxi_init = false;
static float maxi;
static float slop_threshold = 0;
static SignalPoint qrs_onset_point;
static int qrs_on_set_count = 0;
static int qrs_onset_idx = 0;
static bool qrs_on_set_flag = false;
static SignalPoint max;
static SignalPoint max_slop;
static bool max_init = false;
static float last_maxi = 0;
static uint32_t last_point = 0;
SignalPoint So_Chen_detect(SignalPoint,int,float,float);
#endif

100
app/src/basic/adaptive_algorithm.c

@ -0,0 +1,100 @@
#include "adaptive_algorithm.h"
float CalculateMean(float value)
{
value /= 1000.0f;
if(mean_count < MEAN_SIZE){
mean_sum += value;
++mean_count;
}
else{
mean = mean_sum/MEAN_SIZE;
mean_count = 0;
mean_sum = 0;
}
return (mean * 1000.0f);
}
float CalculateRootMeanSquare(float value)
{
value /= 1000.0f;
if(rms_count < RMS_SIZE){
rms_sum += value * value;
++rms_count;
}
else{
rms = sqrt(rms_sum/RMS_SIZE);
rms_count = 0;
rms_sum = 0;
}
return (rms * 1000.0f);
}
float CalculateCoefficientOfVariation(float value)
{
value /= 1000.0f;
if(cv_count < CV_SIZE){
sd += (value - mean) * (value - mean);
++cv_count;
}
else{
sd = sqrt(sd / (CV_SIZE-1));
cv = (sd / mean) * 100;
cv_count = 0;
sd = 0;
}
return cv;
}
void InitPeakDetect(float value,bool emi_first)
{
if(!init_flag){
current_max = value;
current_min = value;
is_detecting_emi = emi_first;
init_flag = true;
}
}
SignalPoint PeakDetect(float value,int index,float gradient,bool *is_peak)
{
if(value > current_max){
max_point = index;
current_max = value;
}
if(value < current_min){
min_point = index;
current_min = value;
}
if(is_detecting_emi && value < (current_max - gradient) ){
is_detecting_emi = false;
current_min = current_max;
min_point = max_point;
*is_peak = true;
peak.value = current_max;
peak.index = max_point;
return peak;
}
else if((!is_detecting_emi) && value > (current_min + gradient))
{
is_detecting_emi = true;
current_max = current_min;
max_point = min_point;
*is_peak = false;
peak.value = current_min;
peak.index = min_point;
return peak;
}
peak.index = -1;
return peak;
}

41
app/src/basic/adaptive_algorithm.h

@ -0,0 +1,41 @@
#ifndef __ALGORITHM__
#define __ALGORITHM__
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <math.h>
#include "QRS.h"
static const uint32_t MEAN_SIZE = SAMPLING_RATE;
static uint32_t mean_count;
static float mean_sum;
static float mean;
static const uint32_t RMS_SIZE = SAMPLING_RATE;
static uint32_t rms_count;
static float rms_sum;
static float rms;
static const uint32_t CV_SIZE = SAMPLING_RATE;
static uint32_t cv_count;
static float sd;
static float cv;
static float current_max;
static float current_min;
static int max_point;
static int min_point;
static SignalPoint peak;
static bool is_detecting_emi;
static bool init_flag = false;
extern float CalculateMean(float);
extern float CalculateRootMeanSquare(float);
extern float CalculateCoefficientOfVariation(float);
extern void InitPeakDetect(float,bool);
extern SignalPoint PeakDetect(float,int,float,bool*);
#endif

2
app/src/board/board.h

@ -32,7 +32,7 @@
#define SAMPLE_MIN_TIME_S (30.0) // 采样最小时间
#define LITTLE_DATA_BLOCK_FRAME_NUM 5 // 每次多少帧上报一次
#define KEEP_STILL_OVERTIME_MS_1P5 2000 // 保持静止超时时间的1/6
#define KEEP_STILL_OVERTIME_MS_1P5 1500 // 保持静止超时时间的1/6
#define LED_GREEN_PIN 9
#define LED_BLUE_PIN 10

4
app/src/display_manager.c

@ -7,11 +7,11 @@
#include "basic/ssd1306/driver_ssd1306.h"
#include "basic/ssd1306/driver_ssd1306_basic.h"
#include "basic/ssd1306/wave_drawer.h"
#include "board/board_battery_state.h"
#include "config.h"
#include "font.h"
#include "heart_wave_sample_service.h"
#include "one_conduction_board.h"
#include "board/board_battery_state.h"
PageState_t g_pageState;
void dsp_mgr_change_to_page(page_t page) { //
@ -360,7 +360,7 @@ void samplePage_schedule() {
int wave_y = (int)hwss_read_val();
int heartrate = (int)hwss_read_heart_rate();
// ZLOGI("samplePage_schedule %d %d %d", capturetime, wave_y, heartrate);
samplePage_update_state(capturetime / 1000, wave_y, 123, count % 10 == 0, count % 5 == 0);
samplePage_update_state(capturetime / 1000, wave_y, hwss_read_heart_rate(), count % 30 == 0, count % 5 == 0);
}
/*******************************************************************************

146
app/src/heart_wave_sample_service.c

@ -2,6 +2,11 @@
#include "app_event.h"
#include "app_event_distribute.h"
#include "basic/FIR.h"
#include "basic/HC_Chen_detect.h"
#include "basic/So_Chen_detect.h"
#include "basic/adaptive_algorithm.h"
#include "basic/Pan_Tompkins_detect.h"
#include "board/board_ecg_sensor.h"
#include "nrfx_timer.h"
#include "one_conduction_board.h"
@ -18,6 +23,116 @@ static uint32_t m_frame_index = 0;
static one_frame_data_t m_sensor_little_frame_cache[LITTLE_DATA_BLOCK_FRAME_NUM];
static uint32_t m_little_frame_index;
static bool m_prestart_flag;
static nrfx_timer_t m_timer = NRFX_TIMER_INSTANCE(HEART_WAVE_SAMPLE_TMR_INSTANCE);
typedef struct {
int heartSignalCnt;
int heart_rate;
uint32_t last_qrs_point;
uint32_t time_cnt;
uint32_t origin_time_cnt;
uint32_t index;
float index_f;
} QRS_t;
QRS_t m_qrs;
void QRS_reset() {
m_qrs.heartSignalCnt = 0;
m_qrs.heart_rate = 0;
m_qrs.time_cnt = 0;
m_qrs.index = 0;
m_qrs.index_f = 0;
FIR_reset_buffer();
}
void QRS_process(float value) {
bool isPeak = false;
#if 1
float result = value;
SignalPoint sp;
sp.value = result;
sp.index = m_qrs.time_cnt;
SignalPoint peak = So_Chen_detect(sp, SAMPLING_RATE * 0.25f, 4, 16);
if (peak.index != -1) {
isPeak = true;
}
#endif
#if 0
isPeak = HC_Chen_detect(value);
#endif
#if 0
static const float CV_LIMIT = 50.0f;
static const float THRESHOLD_FACTOR = 3.0f;
double mean = CalculateMean(value);
double rms = CalculateRootMeanSquare(value);
double cv = CalculateCoefficientOfVariation(value);
double threshold;
if (cv > CV_LIMIT) {
threshold = rms;
} else {
threshold = rms * (cv / 100.0f) * THRESHOLD_FACTOR;
}
bool is_peak;
SignalPoint result;
result = PeakDetect(value, m_qrs.time_cnt, threshold, &is_peak);
if (result.index != -1) {
if (is_peak) {
isPeak = true;
}
}
#endif
#if 0
double result = value;
double bandpass;
double integral;
double square;
bandpass = result;
result = Derivative(result);
result = Squar(result);
square = result;
result = MovingWindowIntegral(result);
integral = result;
SignalPoint peak = ThresholdCalculate(m_qrs.time_cnt,value,bandpass,square,integral);
if(peak.index != -1){
isPeak = true;
}
#endif
if (isPeak) {
uint32_t time_diff = m_qrs.time_cnt - m_qrs.last_qrs_point;
m_qrs.last_qrs_point = m_qrs.time_cnt;
m_qrs.heartSignalCnt++;
// m_qrs.heart_rate = m_qrs.heartSignalCnt;
if (m_qrs.last_qrs_point != 0) {
m_qrs.heart_rate = 60 * (1 / (time_diff * 1.0 / SAMPLING_RATE));
}
}
m_qrs.time_cnt++;
}
static const void compute_heart_rate(float sample_data) {
ZASSERT(SAMPLE_RATE == 500);
m_qrs.index_f = m_frame_index / 500.0 * 360;
if ((m_qrs.index_f - m_qrs.index) > 1) {
m_qrs.index = m_qrs.index_f;
float val = sample_data;
QRS_process(FIR_filter(val));
}
}
static void swap_buffer() {
if (m_capture_buffer == NULL) {
@ -92,14 +207,21 @@ static inline void prvf_light_block_trigger_event() {
}
void nrfx_timer_event_handler(nrf_timer_event_t event_type, void* p_context) { //
uint16_t val = BoardEcgSensor_plod_get_ecg_val(); // 12bit
m_frame_index++;
uint16_t val = BoardEcgSensor_plod_get_ecg_val(); // 12bit
float val_af100 = (float)val / 4096.0f * 100;
if (m_prestart_flag) {
compute_heart_rate(val_af100);
return;
} else {
compute_heart_rate(val_af100);
}
/*******************************************************************************
* ÏÔʾÊý¾Ý¼ÆËã²¢¸³Öµ *
*******************************************************************************/
float val_af100 = (float)val / 4096.0f * 100;
val_af100 = amp_val(val_af100, 50, 1.8f);
m_frame_index++;
val_af100 = amp_val(val_af100, 45, 3.5f);
val_af100 = Filter(&m_filter, val_af100);
m_sensor_display_data = val_af100;
@ -134,7 +256,6 @@ void nrfx_timer_event_handler(nrf_timer_event_t event_type, void* p_context) {
prvf_light_block_cache_clear();
}
}
static const nrfx_timer_t m_timer = NRFX_TIMER_INSTANCE(HEART_WAVE_SAMPLE_TMR_INSTANCE);
void hwss_init(void) {
static bool m_timer_inited = false;
@ -152,20 +273,27 @@ void hwss_init(void) {
// nrfx_timer_init(&m_timer, &timer_cfg, nrfx_timer_event_handler);
ZERROR_CHECK(nrfx_timer_init(&m_timer, &timer_cfg, nrfx_timer_event_handler));
uint32_t timer_ticks = nrfx_timer_ms_to_ticks(&m_timer, 5); // 200HZ
uint32_t timer_ticks = nrfx_timer_ms_to_ticks(&m_timer, 2); //
ZASSERT(SAMPLE_RATE == 500);
nrfx_timer_extended_compare(&m_timer, NRF_TIMER_CC_CHANNEL0, timer_ticks, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, true);
m_timer_inited = true;
}
}
void hwss_uninit(void) { nrfx_timer_disable(&m_timer); }
void hwss_start_capture(void) {
void hwss_pre_start_capture(void) {
m_start_capture_tp = znordic_getpower_on_s();
swap_buffer();
m_frame_index = 0;
QRS_reset();
prvf_light_block_cache_clear();
nrfx_timer_enable(&m_timer);
m_prestart_flag = true;
}
void hwss_start_capture(void) { m_prestart_flag = false; }
void hwss_stop_capture(void) {
nrfx_timer_disable(&m_timer);
m_frame_index = 0;
@ -178,6 +306,8 @@ float hwss_read_val(void) {
__enable_irq();
return val;
}
float hwss_read_heart_rate(void) { return 0; }
float hwss_read_heart_rate(void) { //
return m_qrs.heart_rate;
}
int hwss_has_captured_time_ms() { return (znordic_getpower_on_s() - m_start_capture_tp) * 1000; }

1
app/src/heart_wave_sample_service.h

@ -7,6 +7,7 @@ typedef void (*heart_wave_sample_service_callback_t)(uint16_t *p_data, uint16_t
void hwss_init(void);
void hwss_uninit(void);
void hwss_pre_start_capture(void);
void hwss_start_capture(void);
void hwss_stop_capture(void);

2
app/src/one_conduction_main.c

@ -229,6 +229,7 @@ static void app_event_listener(void* p_event_data, uint16_t event_size) {
if (!BoardBattery_get_charging_state()) {
ds_change_to_state(kdevice_state_keep_still);
dsp_mgr_change_to_preparePage();
hwss_pre_start_capture();
}
}
@ -251,6 +252,7 @@ static void app_event_listener(void* p_event_data, uint16_t event_size) {
if (!BoardEcgSensor_plod_get_connected_state_after_filter()) {
//
state_machine__change_to_home_state();
hwss_stop_capture();
} else {
/*******************************************************************************
* *

2
ify_hrs_protocol

@ -1 +1 @@
Subproject commit da8e07706a41c157a3e42da3f69136f665f5a20f
Subproject commit f2eae40f67048820f37dc5dd3585ca9a75156dfc

1
tools/build.bat

@ -1,3 +1,4 @@
g++ -static -static-libgcc -static-libstdc++ -lwsock32 -lstdc++ .\sample_bin_parse.cpp -o sample_bin_parse.exe
g++ -static -static-libgcc -static-libstdc++ -lwsock32 -lstdc++ text2bin.cpp -o text2bin.exe
g++ -static -static-libgcc -static-libstdc++ -lwsock32 -lstdc++ one_lead_upload_packet_parser.cpp -o one_lead_upload_packet_parser.exe
g++ -static -static-libgcc -static-libstdc++ -lwsock32 -lstdc++ one_lead_realtime_report_parser.cpp -o one_lead_realtime_report_parser.exe

121
tools/one_lead_realtime_report_parser.cpp

@ -0,0 +1,121 @@
#include <fstream>
#include <functional>
#include <iostream>
#include <list>
#include <map>
#include <memory>
#include <set>
#include <sstream>
#include <string>
#include <thread>
#include <vector>
//
#include <winsock2.h>
//
#include <Windows.h>
//
#include <stdio.h>
#pragma comment(lib, "ws2_32.lib")
#define PORT 8988
// #define EACH_FRAME_SIZE 180
// #define DATA_NUM (EACH_FRAME_SIZE / 3)
// 5A A5
// 03 00 65
// 05 00 00 00
// 77 07
// 80 07
// 7F 07
// 88 07
// 8D 07
// 5B B5
#pragma pack(1)
typedef struct parse_packet_to_csv {
uint8_t h0;
uint8_t h1;
uint8_t header[3];
uint8_t index[4];
uint16_t data[5];
uint8_t e0;
uint8_t e1;
};
#pragma pack()
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
uint16_t chdatacache[5];
void parse_chdatacache(parse_packet_to_csv *packet) {
chdatacache[0] = packet->data[0];
chdatacache[1] = packet->data[1];
chdatacache[2] = packet->data[2];
chdatacache[3] = packet->data[3];
chdatacache[4] = packet->data[4];
}
int main(int argc, char *argv[]) {
/**
* @brief
* raw.txt
* raw.txt全部内容
*
* 16
* raw.bin
*/
std::ifstream ifs("Data.txt");
std::string str((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
ifs.close();
std::string str2;
for (auto &c : str) {
if (c == ' ' || c == '\n' || c == '\r') {
continue;
}
str2.push_back(c);
}
std::ofstream ofs("Data.bin", std::ios::binary);
for (size_t i = 0; i < str2.size(); i += 2) {
std::string str3 = str2.substr(i, 2);
char c = (char)std::stoi(str3, nullptr, 16);
ofs.write(&c, 1);
}
ofs.close();
/**
* @brief
*
*/
{
std::ifstream ifs("Data.bin", std::ios::binary);
std::vector<char> buffer((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
ifs.close();
printf("buffer.size() = %d\n", buffer.size());
std::ofstream ofs("Data.csv");
for (size_t i = 0; i < buffer.size();) {
parse_packet_to_csv *packet = (parse_packet_to_csv *)&buffer[i];
if (packet->h0 == 0x5A && packet->h1 == 0xA5 && packet->e0 == 0x5B && packet->e1 == 0xB5) {
parse_chdatacache(packet);
for (size_t i = 0; i < ARRAY_SIZE(chdatacache); i += 1) {
ofs << chdatacache[i] << "\n";
}
i += sizeof(parse_packet_to_csv);
} else {
i++;
}
}
}
printf("parse over\n");
while (true) {
std::this_thread::sleep_for(std::chrono::seconds(1));
}
}

BIN
tools/one_lead_realtime_report_parser.exe

BIN
tools/one_lead_upload_packet_parser.exe

BIN
tools/sample_bin_parse.exe

BIN
tools/text2bin.exe

Loading…
Cancel
Save