Coverage for src/cgse_dummy/sim_data.py: 78%

55 statements  

« prev     ^ index     » next       coverage.py v7.10.6, created at 2025-09-11 18:19 +0200

1import time 

2 

3import numpy as np 

4from datetime import datetime, timedelta 

5 

6 

7def _simulate_temperature_data( 

8 start_temp=25, # Room temperature (ºC) 

9 first_target=-70, # First cooling target (ºC) 

10 second_target=-40, # Heating target (ºC) 

11 final_target=-180, # Final cooling target (ºC) 

12 total_time=720, # Total time in minutes (12 hours) 

13 sample_rate=10, # Seconds between measurements 

14) -> tuple[list[datetime], list[float]]: 

15 """ 

16 Simulate temperature sensor data with a plateau phase and a cooling period. 

17 

18 The default arguments will generate temperature data for twelve hours cooling 

19 down from room temperature to -180ºC. We introduced two plateau of one hour 

20 duration, (1) the first plateau at -70ºC starts after one hour of cooling and 

21 (2) the second plateau at -40ºC starts after heating for one hour and twenty 

22 minutes. After the second plateau we cool down until -180ºC. 

23 

24 This code was generated by Claude 3.5 Sonnet with small adjustments. 

25 

26 Returns: 

27 - timestamp and temperature as a tuple. 

28 """ 

29 # Calculate number of samples 

30 n_samples = int((total_time * 60) / sample_rate) 

31 

32 # Create time array in minutes 

33 time = np.linspace(0, total_time, n_samples) 

34 

35 # Initialize temperature array 

36 temp = np.zeros(n_samples) 

37 

38 # Define phase transition points (in minutes) 

39 phase1_end = 60 # First cooling phase 

40 phase2_end = 120 # First plateau 

41 phase3_end = 200 # Heating phase 

42 phase4_end = 260 # Second plateau 

43 

44 # Find indices for different phases 

45 idx1 = np.where(time >= phase1_end)[0][0] 

46 idx2 = np.where(time >= phase2_end)[0][0] 

47 idx3 = np.where(time >= phase3_end)[0][0] 

48 idx4 = np.where(time >= phase4_end)[0][0] 

49 

50 # Phase 1: Initial cooling to -70°C (exponential) 

51 initial_time = time[:idx1] 

52 temp[:idx1] = start_temp + (first_target - start_temp) * ( 

53 1 - np.exp(-initial_time / 15) # Time constant for first cooling 

54 ) 

55 

56 # Phase 2: First plateau at -70°C 

57 temp[idx1:idx2] = first_target 

58 

59 # Phase 3: Heating to -40°C (exponential approach) 

60 heating_time = time[idx2:idx3] - time[idx2] 

61 temp[idx2:idx3] = first_target + (second_target - first_target) * ( 

62 1 - np.exp(-heating_time / 10) # Time constant for heating 

63 ) 

64 

65 # Phase 4: Second plateau at -40°C 

66 temp[idx3:idx4] = second_target 

67 

68 # Phase 5: Final cooling to -180°C 

69 final_time = time[idx4:] - time[idx4] 

70 temp[idx4:] = second_target + (final_target - second_target) * ( 

71 1 - np.exp(-final_time / 100) # Time constant for final cooling 

72 ) 

73 

74 # Add different levels of noise for each phase 

75 base_noise = np.random.normal(0, 0.2, n_samples) 

76 

77 # Add occasional spikes 

78 spike_locations = np.random.choice( 

79 n_samples, size=int(n_samples * 0.005), replace=False 

80 ) 

81 spike_noise = np.zeros(n_samples) 

82 spike_noise[spike_locations] = np.random.normal(0, 3, size=len(spike_locations)) 

83 

84 # Add slow drift 

85 drift = np.sin(np.linspace(0, 8 * np.pi, n_samples)) * 0.8 

86 

87 # Combine all noise components 

88 total_noise = base_noise + spike_noise + drift 

89 temp += total_noise 

90 

91 # Create timestamps 

92 start_time = datetime.now() 

93 timestamps = [ 

94 start_time + timedelta(seconds=i * sample_rate) for i in range(n_samples) 

95 ] 

96 

97 return timestamps, temp.tolist() 

98 

99 

100class SimulatedTemperature: 

101 """ 

102 This is an iterator over simulated temperature data. 

103 

104 Generate simulate temperature sensor data with a plateau phase and a cooling period. 

105 

106 The default arguments will generate temperature data for twelve hours cooling 

107 down from room temperature to -180ºC. We introduced two plateau of one hour 

108 duration, (1) the first plateau at -70ºC starts after one hour of cooling and 

109 (2) the second plateau at -40ºC starts after heating for one hour and twenty 

110 minutes. After the second plateau we cool down until -180ºC. 

111 

112 """ 

113 

114 def __init__(self): 

115 self.timestamps: list[datetime] 

116 self.temperatures: list[float] 

117 

118 self.timestamps, self.temperatures = _simulate_temperature_data() 

119 

120 self.count: int = len(self.timestamps) 

121 """The number of data points.""" 

122 self.idx: int = 0 

123 """A pointer in the list of data points, the next data point to return.""" 

124 

125 def __iter__(self): 

126 return self 

127 

128 def __next__(self): 

129 x, y = self.timestamps[self.idx], self.temperatures[self.idx] 

130 if self.idx >= self.count: 

131 raise StopIteration 

132 self.idx += 1 

133 return x, y 

134 

135 

136if __name__ == "__main__": 136 ↛ 142line 136 didn't jump to line 142 because the condition on line 136 was never true

137 

138 # for timestamp, temperature in SimulatedTemperature(): 

139 # print(f"{timestamp = }, {temperature = }") 

140 # time.sleep(1.0) 

141 

142 import matplotlib.pyplot as plt 

143 

144 x, y = _simulate_temperature_data() 

145 

146 plt.plot(x, y) 

147 plt.show()