transitsong 0.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- transitsong-0.0.1/PKG-INFO +9 -0
- transitsong-0.0.1/README.md +2 -0
- transitsong-0.0.1/pyproject.toml +21 -0
- transitsong-0.0.1/setup.cfg +4 -0
- transitsong-0.0.1/tests/tests.py +12 -0
- transitsong-0.0.1/transitsong/__init__.py +0 -0
- transitsong-0.0.1/transitsong/main.py +243 -0
- transitsong-0.0.1/transitsong.egg-info/PKG-INFO +9 -0
- transitsong-0.0.1/transitsong.egg-info/SOURCES.txt +10 -0
- transitsong-0.0.1/transitsong.egg-info/dependency_links.txt +1 -0
- transitsong-0.0.1/transitsong.egg-info/requires.txt +6 -0
- transitsong-0.0.1/transitsong.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = [
|
|
3
|
+
"setuptools",
|
|
4
|
+
"numpy",
|
|
5
|
+
"cython",
|
|
6
|
+
"wheel"
|
|
7
|
+
]
|
|
8
|
+
build-backend = "setuptools.build_meta"
|
|
9
|
+
|
|
10
|
+
[project]
|
|
11
|
+
name = "transitsong"
|
|
12
|
+
version = "0.0.1"
|
|
13
|
+
|
|
14
|
+
dependencies = [
|
|
15
|
+
"numpy",
|
|
16
|
+
"sounddevice",
|
|
17
|
+
"lightkurve",
|
|
18
|
+
"matplotlib",
|
|
19
|
+
"scipy",
|
|
20
|
+
"moviepy"
|
|
21
|
+
]
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from transitsong.main import Transit
|
|
2
|
+
|
|
3
|
+
# test that lightcurve downloads ok!
|
|
4
|
+
|
|
5
|
+
def test_lc_download():
|
|
6
|
+
tic = 124029677
|
|
7
|
+
sector = 33
|
|
8
|
+
|
|
9
|
+
transit = Transit(tic, sector)
|
|
10
|
+
|
|
11
|
+
assert len(transit.norm_flux) == len(transit.time), "Time and flux arrays are not the same length"
|
|
12
|
+
assert len(transit.norm_flux) > 0, "norm flux array, time array is of length 0"
|
|
File without changes
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import lightkurve as lk
|
|
2
|
+
import matplotlib.pyplot as plt
|
|
3
|
+
import numpy as np
|
|
4
|
+
from scipy.io.wavfile import write
|
|
5
|
+
import sounddevice as sd
|
|
6
|
+
import matplotlib.animation as animation
|
|
7
|
+
from moviepy import *
|
|
8
|
+
from moviepy.video.fx import MultiplySpeed
|
|
9
|
+
import os
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
# set matplotlib preferences
|
|
13
|
+
#plt.style.use(path + 'text.mplstyle')
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Transit:
|
|
17
|
+
"""A program that makes an animation with audio for an input TESS lightcurve.
|
|
18
|
+
|
|
19
|
+
Attributes:
|
|
20
|
+
tic (int or str): TESS Input Catalog number
|
|
21
|
+
sector (int): TESS sector number
|
|
22
|
+
window (list of length 2): start and end days of sector
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def __init__(self, tic, sector, window = None):
|
|
26
|
+
self.tic = tic
|
|
27
|
+
self.sector = sector
|
|
28
|
+
self.window = window
|
|
29
|
+
|
|
30
|
+
try:
|
|
31
|
+
search_result = lk.search_lightcurve(
|
|
32
|
+
f"TIC {tic}",
|
|
33
|
+
mission="TESS",
|
|
34
|
+
author="SPOC",
|
|
35
|
+
sector =[sector] )
|
|
36
|
+
|
|
37
|
+
lc = search_result.download()
|
|
38
|
+
self.success = True
|
|
39
|
+
|
|
40
|
+
except Exception as e:
|
|
41
|
+
self.success = False
|
|
42
|
+
raise ValueError("Failed to load lightcurve for TIC {} in sector {}: {}".format(tic, sector, str(e)))
|
|
43
|
+
|
|
44
|
+
print("Successfully loaded lightcurve for TIC {} in sector {}".format(tic, sector))
|
|
45
|
+
|
|
46
|
+
lc_binned = lc.bin(time_bin_size=0.01)
|
|
47
|
+
|
|
48
|
+
lc_binned = lc_binned.normalize()
|
|
49
|
+
|
|
50
|
+
lc_fluxes = lc_binned.flux.value
|
|
51
|
+
lc_times = lc_binned.time.value
|
|
52
|
+
|
|
53
|
+
#med_value = np.nanmedian(lc_fluxes)
|
|
54
|
+
|
|
55
|
+
#lc_normflux = lc_fluxes/med_value
|
|
56
|
+
|
|
57
|
+
self.time = lc_times
|
|
58
|
+
self.norm_flux = lc_fluxes
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
if window is not None:
|
|
62
|
+
new_times = []
|
|
63
|
+
new_fluxes = []
|
|
64
|
+
|
|
65
|
+
for i in range(len(self.time)):
|
|
66
|
+
if self.time[i] > window[0] and self.time[i] < window[1]:
|
|
67
|
+
new_times.append(self.time[i])
|
|
68
|
+
new_fluxes.append(self.norm_flux[i])
|
|
69
|
+
|
|
70
|
+
self.time = np.array(new_times)
|
|
71
|
+
self.norm_flux = np.array(new_fluxes)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def make_sound_arr(self, max_val=900, min_val=200):
|
|
75
|
+
"""Sound array
|
|
76
|
+
|
|
77
|
+
Make sound array as .wav file, higher pitch corresponds to higher flux. Saves to subdirectory "song."
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
max_val (int or float): maximum Hz frequency
|
|
81
|
+
min_val (int or float): minimum Hz frequency
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
Nothing.
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
mapped_flux = (self.norm_flux - np.nanmin(self.norm_flux)) / (np.nanmax(self.norm_flux) - np.nanmin(self.norm_flux)) * (max_val - min_val) + min_val
|
|
88
|
+
|
|
89
|
+
self.mapped_flux = mapped_flux
|
|
90
|
+
|
|
91
|
+
audio = []
|
|
92
|
+
len_points = []
|
|
93
|
+
|
|
94
|
+
samplerate = 44100
|
|
95
|
+
duration = 0.05
|
|
96
|
+
|
|
97
|
+
for flux in mapped_flux:
|
|
98
|
+
frequency = flux
|
|
99
|
+
t = np.linspace(0, duration, int(samplerate * duration), endpoint=False)
|
|
100
|
+
|
|
101
|
+
audio_signal = np.sin(2*np.pi*frequency*t)
|
|
102
|
+
audio_signal = audio_signal[~np.isnan(audio_signal)]
|
|
103
|
+
len_points.append(len(audio_signal))
|
|
104
|
+
|
|
105
|
+
audio.extend(audio_signal)
|
|
106
|
+
|
|
107
|
+
audio_arr = np.array(audio)
|
|
108
|
+
self.audio_arr = audio_arr
|
|
109
|
+
|
|
110
|
+
song_path = os.getcwd() + "/song/"
|
|
111
|
+
self.song_path = song_path
|
|
112
|
+
if not os.path.isdir(song_path):
|
|
113
|
+
directory = Path(song_path)
|
|
114
|
+
directory.mkdir(parents=True, exist_ok=True)
|
|
115
|
+
|
|
116
|
+
write(song_path + f"TIC{self.tic}_S{self.sector}_SONG.wav", samplerate, audio_arr)
|
|
117
|
+
|
|
118
|
+
# colors
|
|
119
|
+
cmap = plt.cm.magma
|
|
120
|
+
|
|
121
|
+
sorted_args = np.argsort(self.norm_flux)
|
|
122
|
+
|
|
123
|
+
values = np.linspace(0, 1, len(self.norm_flux))
|
|
124
|
+
colors = np.empty((len(self.norm_flux), 4)) # 4 for RGBA
|
|
125
|
+
colors[sorted_args] = cmap(values)
|
|
126
|
+
|
|
127
|
+
self.colors= colors
|
|
128
|
+
|
|
129
|
+
#sd.play(audio_arr, samplerate)
|
|
130
|
+
#sd.wait()
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def make_video(self):
|
|
134
|
+
"""Video file
|
|
135
|
+
|
|
136
|
+
Makes animated plot of lightcurve, lighter color corresponds to higher flux. Saves as .mp4 in subdirectory "dance".
|
|
137
|
+
|
|
138
|
+
Args:
|
|
139
|
+
None.
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
Nothing.
|
|
143
|
+
"""
|
|
144
|
+
# Original time
|
|
145
|
+
x_raw = self.time
|
|
146
|
+
y_raw = self.norm_flux
|
|
147
|
+
|
|
148
|
+
# Removing nans
|
|
149
|
+
y = y_raw[~np.isnan(y_raw)]
|
|
150
|
+
x = x_raw[~np.isnan(y_raw)]
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
fig, ax = plt.subplots()
|
|
154
|
+
line, = ax.plot([], [], marker='o', linestyle='', color='b')
|
|
155
|
+
ax.set_xlabel('Time (days)')
|
|
156
|
+
ax.set_ylabel('Normalized Flux')
|
|
157
|
+
ax.set_xlim(self.time[0], self.time[-1])
|
|
158
|
+
ax.set_ylim(np.nanmin(self.norm_flux)-0.005, np.nanmax(self.norm_flux)+0.005)
|
|
159
|
+
ax.set_title(f"TIC {self.tic} - Sector {self.sector}")
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def update(i):
|
|
163
|
+
|
|
164
|
+
# line.set_data(x[:frame], y[:frame])
|
|
165
|
+
# line.set_color(self.colors[frame])
|
|
166
|
+
|
|
167
|
+
plt.scatter(x[:i], y[:i], color=self.colors[i], edgecolor="k", linewidth=0.5)
|
|
168
|
+
|
|
169
|
+
# return line,
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
ani = animation.FuncAnimation(
|
|
173
|
+
fig, update, frames=len(x), interval=50, repeat=True
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
dance_path = os.getcwd() + "/dance/"
|
|
177
|
+
self.dance_path = dance_path
|
|
178
|
+
if not os.path.isdir(dance_path):
|
|
179
|
+
directory = Path(dance_path)
|
|
180
|
+
directory.mkdir(parents=True, exist_ok=True)
|
|
181
|
+
|
|
182
|
+
ani.save(dance_path + f"TIC{self.tic}_S{self.sector}_DANCE.mp4", writer='ffmpeg', fps=30)
|
|
183
|
+
#plt.show()
|
|
184
|
+
|
|
185
|
+
def combine(self):
|
|
186
|
+
video_clip = VideoFileClip(self.dance_path + f"TIC{self.tic}_S{self.sector}_DANCE.mp4", audio=False)
|
|
187
|
+
audio_clip = AudioFileClip(self.song_path + f"TIC{self.tic}_S{self.sector}_SONG.wav")
|
|
188
|
+
|
|
189
|
+
video_factor = video_clip.duration / audio_clip.duration
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
video_clip = video_clip.with_effects([MultiplySpeed(video_factor)])
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
final_clip = video_clip.with_audio(audio_clip)
|
|
196
|
+
|
|
197
|
+
song_and_dance_path = os.getcwd() + "/song_and_dance/"
|
|
198
|
+
if not os.path.isdir(song_and_dance_path):
|
|
199
|
+
directory = Path(song_and_dance_path)
|
|
200
|
+
directory.mkdir(parents=True, exist_ok=True)
|
|
201
|
+
|
|
202
|
+
final_clip.write_videofile(
|
|
203
|
+
song_and_dance_path + f"TIC{self.tic}_S{self.sector}_FINAL.mp4",
|
|
204
|
+
codec="libx264",
|
|
205
|
+
audio_codec="aac",
|
|
206
|
+
temp_audiofile="temp-audio.m4a",
|
|
207
|
+
remove_temp=True,
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
video_clip.close()
|
|
212
|
+
audio_clip.close()
|
|
213
|
+
final_clip.close()
|
|
214
|
+
|
|
215
|
+
#def play_song(Transit):
|
|
216
|
+
# here is where we do the simultaneous thing?
|
|
217
|
+
#tic = 124029677
|
|
218
|
+
#sector = 33
|
|
219
|
+
#window = [2217, 2220]
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
# tic = 55652896
|
|
224
|
+
# sector = 63 #originally did 38
|
|
225
|
+
# window = [2340, 2341]
|
|
226
|
+
|
|
227
|
+
# tic = 149601126
|
|
228
|
+
# sector = 32 #96
|
|
229
|
+
# window = [2196, 2198.5]
|
|
230
|
+
|
|
231
|
+
# tic = 263930790
|
|
232
|
+
# sector = 73
|
|
233
|
+
# window = [3293, 3299]
|
|
234
|
+
|
|
235
|
+
# planet = Transit(tic, sector, window=window)
|
|
236
|
+
# planet.make_sound_arr()
|
|
237
|
+
# planet.make_video()
|
|
238
|
+
# planet.combine()
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
#55652896, 38, 63
|
|
242
|
+
|
|
243
|
+
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
tests/tests.py
|
|
4
|
+
transitsong/__init__.py
|
|
5
|
+
transitsong/main.py
|
|
6
|
+
transitsong.egg-info/PKG-INFO
|
|
7
|
+
transitsong.egg-info/SOURCES.txt
|
|
8
|
+
transitsong.egg-info/dependency_links.txt
|
|
9
|
+
transitsong.egg-info/requires.txt
|
|
10
|
+
transitsong.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
transitsong
|